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

Diff for /src/usr.bin/cvs/file.c between version 1.137 and 1.138

version 1.137, 2006/04/14 02:45:35 version 1.138, 2006/05/27 03:30:30
Line 1 
Line 1 
 /*      $OpenBSD$       */  /*      $OpenBSD$       */
 /*  /*
    * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>   * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
  * All rights reserved.   * All rights reserved.
  *   *
Line 34 
Line 35 
   
 #define CVS_CHAR_ISMETA(c)      ((c == '*') || (c == '?') || (c == '['))  #define CVS_CHAR_ISMETA(c)      ((c == '*') || (c == '?') || (c == '['))
   
 /* ignore pattern */  
 struct cvs_ignpat {  
         char                            ip_pat[MAXNAMLEN];  
         int                             ip_flags;  
         TAILQ_ENTRY(cvs_ignpat)         ip_list;  
 };  
   
 /*  /*
  * Standard patterns to ignore.   * Standard patterns to ignore.
  */   */
Line 48 
Line 42 
         ".",          ".",
         "..",          "..",
         "*.o",          "*.o",
         "*.so",  
         "*.a",          "*.a",
         "*.bak",          "*.bak",
         "*.orig",          "*.orig",
Line 68 
Line 61 
         "*~",          "*~",
         "_$*",          "_$*",
         "*$",          "*$",
 #ifdef OLD_SMELLY_CRUFT  
         "RCSLOG",  
         "tags",  
         "TAGS",  
         "RCS",  
         "SCCS",  
         "cvslog.*",     /* to ignore CVS_CLIENT_LOG output */  
         "#*",  
         ",*",  
 #endif  
 };  };
   
 /*  struct ignore_head cvs_ign_pats;
  * Entries in the CVS/Entries file with a revision of '0' have only been  struct ignore_head dir_ign_pats;
  * added.  Compare against this revision to see if this is the case  
  */  
 static RCSNUM *cvs_addedrev;  
   
 TAILQ_HEAD(, cvs_ignpat)        cvs_ign_pats;  static int      cvs_file_cmpname(const char *, const char *);
   
 static int cvs_file_getdir(CVSFILE *, int, int (*)(CVSFILE *, void *),  void
     void *, int);  
   
 static int       cvs_load_dirinfo(CVSFILE *, int);  
 static int       cvs_file_sort(struct cvs_flist *, u_int);  
 static int       cvs_file_cmp(const void *, const void *);  
 static int       cvs_file_cmpname(const char *, const char *);  
 static CVSFILE  *cvs_file_alloc(const char *, u_int);  
 static CVSFILE  *cvs_file_lget(const char *, int, CVSFILE *, CVSENTRIES *,  
                     struct cvs_ent *);  
   
 /*  
  * cvs_file_init()  
  *  
  */  
 int  
 cvs_file_init(void)  cvs_file_init(void)
 {  {
         int i, l;          int i, l;
         size_t len;  
         char path[MAXPATHLEN], buf[MAXNAMLEN];  
         FILE *ifp;          FILE *ifp;
           size_t len;
           char *path, *buf;
   
           path = xmalloc(MAXPATHLEN);
           buf = xmalloc(MAXNAMLEN);
   
         TAILQ_INIT(&cvs_ign_pats);          TAILQ_INIT(&cvs_ign_pats);
           TAILQ_INIT(&dir_ign_pats);
   
         if ((cvs_addedrev = rcsnum_parse("0")) == NULL)  
                 return (-1);  
   
         /* standard patterns to ignore */          /* standard patterns to ignore */
         for (i = 0; i < (int)(sizeof(cvs_ign_std)/sizeof(char *)); i++)          for (i = 0; i < (int)(sizeof(cvs_ign_std)/sizeof(char *)); i++)
                 cvs_file_ignore(cvs_ign_std[i]);                  cvs_file_ignore(cvs_ign_std[i], &cvs_ign_pats);
   
         /* read the cvsignore file in the user's home directory, if any */          /* read the cvsignore file in the user's home directory, if any */
         l = snprintf(path, sizeof(path), "%s/.cvsignore", cvs_homedir);          l = snprintf(path, MAXPATHLEN, "%s/.cvsignore", cvs_homedir);
         if (l == -1 || l >= (int)sizeof(path)) {          if (l == -1 || l >= MAXPATHLEN)
                 errno = ENAMETOOLONG;                  fatal("overflow in cvs_file_init");
                 cvs_log(LP_ERRNO, "%s", path);  
                 return (-1);  
         }  
   
         ifp = fopen(path, "r");          ifp = fopen(path, "r");
         if (ifp == NULL) {          if (ifp == NULL) {
Line 134 
Line 97 
                         cvs_log(LP_ERRNO,                          cvs_log(LP_ERRNO,
                             "failed to open user's cvsignore file `%s'", path);                              "failed to open user's cvsignore file `%s'", path);
         } else {          } else {
                 while (fgets(buf, (int)sizeof(buf), ifp) != NULL) {                  while (fgets(buf, MAXNAMLEN, ifp) != NULL) {
                         len = strlen(buf);                          len = strlen(buf);
                         if (len == 0)                          if (len == 0)
                                 continue;                                  continue;
                         if (buf[len - 1] != '\n') {  
                           if (buf[len - 1] != '\n')
                                 cvs_log(LP_ERR, "line too long in `%s'", path);                                  cvs_log(LP_ERR, "line too long in `%s'", path);
                         }  
                         buf[--len] = '\0';                          buf[--len] = '\0';
                         cvs_file_ignore(buf);                          cvs_file_ignore(buf, &cvs_ign_pats);
                 }                  }
   
                 (void)fclose(ifp);                  (void)fclose(ifp);
         }          }
   
         return (0);          xfree(path);
           xfree(buf);
 }  }
   
 /*  void
  * cvs_file_ignore()  cvs_file_ignore(const char *pat, struct ignore_head *list)
  *  
  * Add the pattern <pat> to the list of patterns for files to ignore.  
  * Returns 0 on success, or -1 on failure.  
  */  
 int  
 cvs_file_ignore(const char *pat)  
 {  {
         char *cp;          char *cp;
           size_t len;
         struct cvs_ignpat *ip;          struct cvs_ignpat *ip;
   
         ip = xmalloc(sizeof(*ip));          ip = xmalloc(sizeof(*ip));
         strlcpy(ip->ip_pat, pat, sizeof(ip->ip_pat));          len = strlcpy(ip->ip_pat, pat, sizeof(ip->ip_pat));
           if (len >= sizeof(ip->ip_pat))
                   fatal("cvs_file_ignore: truncation of pattern '%s'", pat);
   
         /* check if we will need globbing for that pattern */          /* check if we will need globbing for that pattern */
         ip->ip_flags = CVS_IGN_STATIC;          ip->ip_flags = CVS_IGN_STATIC;
Line 174 
Line 137 
                 }                  }
         }          }
   
         TAILQ_INSERT_TAIL(&cvs_ign_pats, ip, ip_list);          TAILQ_INSERT_TAIL(list, ip, ip_list);
   
         return (0);  
 }  }
   
 /*  
  * cvs_file_chkign()  
  *  
  * Returns 1 if the filename <file> is matched by one of the ignore  
  * patterns, or 0 otherwise.  
  */  
 int  int
 cvs_file_chkign(const char *file)  cvs_file_chkign(const char *file)
 {  {
Line 203 
Line 158 
                         return (1);                          return (1);
         }          }
   
           TAILQ_FOREACH(ip, &dir_ign_pats, ip_list) {
                   if (ip->ip_flags & CVS_IGN_STATIC) {
                           if (cvs_file_cmpname(file, ip->ip_pat) == 0)
                                   return (1);
                   } else if (fnmatch(ip->ip_pat, file, flags) == 0)
                           return (1);
           }
   
         return (0);          return (0);
 }  }
   
 /*  void
  * cvs_file_create()  cvs_file_run(int argc, char **argv, struct cvs_recursion *cr)
  *  
  * Create a new file whose path is specified in <path> and of type <type>.  
  * If the type is DT_DIR, the CVS administrative repository and files will be  
  * created.  
  * Returns the created file on success, or NULL on failure.  
  */  
 CVSFILE *  
 cvs_file_create(CVSFILE *parent, const char *path, u_int type, mode_t mode)  
 {  {
         int fd, l;          int i;
         char fp[MAXPATHLEN], repo[MAXPATHLEN];          struct cvs_flisthead fl;
         CVSFILE *cfp;  
   
         cfp = cvs_file_alloc(path, type);          TAILQ_INIT(&fl);
         if (cfp == NULL)  
                 return (NULL);  
   
         l = 0;          for (i = 0; i < argc; i++)
         cfp->cf_mode = mode;                  cvs_file_get(argv[i], &fl);
         cfp->cf_parent = parent;  
   
         if (type == DT_DIR) {          cvs_file_walklist(&fl, cr);
                 cfp->cf_root = cvsroot_get(path);          cvs_file_freelist(&fl);
                 if (cfp->cf_root == NULL) {  
                         cvs_file_free(cfp);  
                         return (NULL);  
                 }  
   
                 if (cvs_repo_base != NULL) {  
                         cvs_file_getpath(cfp, fp, sizeof(fp));  
                         l = snprintf(repo, sizeof(repo), "%s/%s", cvs_repo_base,  
                             fp);  
                 } else {  
                         cvs_file_getpath(cfp, repo, sizeof(repo));  
                         l = 0;  
                 }  
   
                 if (l == -1 || l >= (int)sizeof(repo)) {  
                         errno = ENAMETOOLONG;  
                         cvs_log(LP_ERRNO, "%s", repo);  
                         cvs_file_free(cfp);  
                         return (NULL);  
                 }  
   
                 cfp->cf_repo = xstrdup(repo);  
                 if ((mkdir(path, mode) == -1 && errno != EEXIST) ||  
                     cvs_mkadmin(path, cfp->cf_root->cr_str, cfp->cf_repo,  
                     NULL, NULL, 0) < 0) {  
                         cvs_file_free(cfp);  
                         return (NULL);  
                 }  
         } else {  
                 fd = open(path, O_WRONLY|O_CREAT|O_EXCL, mode);  
                 if (fd == -1) {  
                         cvs_file_free(cfp);  
                         return (NULL);  
                 }  
                 (void)close(fd);  
         }  
   
         return (cfp);  
 }  }
   
   struct cvs_filelist *
 /*  cvs_file_get(const char *name, struct cvs_flisthead *fl)
  * cvs_file_copy()  
  *  
  * Allocate space to create a copy of the file <orig>.  The copy inherits all  
  * of the original's attributes, but does not inherit its children if the  
  * original file is a directory.  Note that files copied using this mechanism  
  * are linked to their parent, but the parent has no link to the file.  This  
  * is so cvs_file_getpath() works.  
  * Returns the copied file on success, or NULL on failure.  The returned  
  * structure should be freed using cvs_file_free().  
  */  
 CVSFILE *  
 cvs_file_copy(CVSFILE *orig)  
 {  {
         char path[MAXPATHLEN];          const char *p;
         CVSFILE *cfp;          struct cvs_filelist *l;
   
         cvs_file_getpath(orig, path, sizeof(path));          for (p = name; p[0] == '.' && p[1] == '/';)
                   p += 2;
   
         cfp = cvs_file_alloc(path, orig->cf_type);          TAILQ_FOREACH(l, fl, flist)
         if (cfp == NULL)                  if (!strcmp(l->file_path, p))
                 return (NULL);                          return (l);
   
         cfp->cf_parent = orig->cf_parent;          l = (struct cvs_filelist *)xmalloc(sizeof(*l));
         cfp->cf_mode = orig->cf_mode;          l->file_path = xstrdup(p);
         cfp->cf_cvstat = orig->cf_cvstat;  
   
         if (orig->cf_type == DT_REG) {          TAILQ_INSERT_TAIL(fl, l, flist);
                 cfp->cf_etime = orig->cf_etime;          return (l);
                 cfp->cf_mtime = orig->cf_mtime;  
         } else if (orig->cf_type == DT_DIR) {  
                 /* XXX copy CVS directory attributes */  
         }  
   
         return (cfp);  
 }  }
   
   struct cvs_file *
 /*  cvs_file_get_cf(const char *d, const char *f, int fd, int type)
  * cvs_file_get()  
  *  
  * Load a cvs_file structure with all the information pertaining to the file  
  * <path>.  
  * The <flags> parameter specifies various flags that alter the behaviour of  
  * the function.  The CF_RECURSE flag causes the function to recursively load  
  * subdirectories when <path> is a directory.  
  * The CF_SORT flag causes the files to be sorted in alphabetical order upon  
  * loading.  The special case of "." as a path specification generates  
  * recursion for a single level and is equivalent to calling cvs_file_get() on  
  * all files of that directory.  
  * Returns a pointer to the cvs file structure, which must later be freed  
  * with cvs_file_free().  
  */  
   
 int  
 cvs_file_get(const char *path, int flags, int (*cb)(CVSFILE *, void *),  
     void *arg, struct cvs_flist *list)  
 {  {
         char *files[1];          int l;
           struct cvs_file *cf;
           char *p, *rpath;
   
         *(const char **)files = path;          rpath = xmalloc(MAXPATHLEN);
         return cvs_file_getspec(files, 1, flags, cb, arg, list);  
 }  
   
           l = snprintf(rpath, MAXPATHLEN, "%s/%s", d, f);
           if (l == -1 || l >= MAXPATHLEN)
                   fatal("cvs_file_get_cf: overflow");
   
 /*          for (p = rpath; p[0] == '.' && p[1] == '/';)
  * cvs_file_getspec()                  p += 2;
  *  
  * Obtain the info about the supplied files or directories.  
  */  
 int  
 cvs_file_getspec(char **fspec, int fsn, int flags, int (*cb)(CVSFILE *, void *),  
     void *arg, struct cvs_flist *list)  
 {  
         int i, freecf;  
         char pcopy[MAXPATHLEN];  
         CVSFILE *cf;  
         extern char *cvs_rootstr;  
   
         freecf = (list == NULL);          cf = (struct cvs_file *)xmalloc(sizeof(*cf));
         cvs_error = CVS_EX_DATA;          memset(cf, 0, sizeof(*cf));
   
         /* init the list */          cf->file_name = xstrdup(f);
         if (list != NULL)          cf->file_wd = xstrdup(d);
                 SIMPLEQ_INIT(list);          cf->file_path = xstrdup(p);
           cf->fd = fd;
           cf->repo_fd = -1;
           cf->file_type = type;
           cf->file_status = cf->file_flags = 0;
           cf->file_ent = NULL;
   
         /*          xfree(rpath);
          * Fetch the needed information about ".", so we can setup a few          return (cf);
          * things to get ourselfs going.  
          */  
         cf = cvs_file_lget(".", 0, NULL, NULL, NULL);  
         if (cf == NULL) {  
                 cvs_log(LP_ERR, "failed to obtain '.' information");  
                 return (-1);  
         }  
   
         /*  
          * save the base repository path so we can use it to create  
          * the correct full repopath later on.  
          */  
         if (cf->cf_repo != NULL) {  
                 if (cvs_repo_base != NULL)  
                         xfree(cvs_repo_base);  
                 cvs_repo_base = xstrdup(cf->cf_repo);  
         }  
   
         /*  
          * This will go away when we have support for multiple Roots.  
          */  
         if (cvs_rootstr == NULL && cf->cf_root != NULL) {  
                 cvs_rootstr = xstrdup(cf->cf_root->cr_str);  
         }  
   
         cvs_error = CVS_EX_OK;  
   
         /*  
          * Since some commands don't require any files to operate  
          * we can stop right here for those.  
          */  
         if (cf->cf_root != NULL) {  
                 if (cf->cf_root->cr_method != CVS_METHOD_LOCAL &&  
                     cvs_cmdop == CVS_OP_CHECKOUT) {  
                         cvs_file_free(cf);  
                         return (0);  
                 }  
         }  
   
         cvs_file_free(cf);  
   
         if (cvs_cmdop == CVS_OP_VERSION)  
                 return (0);  
   
         for (i = 0; i < fsn; i++) {  
                 strlcpy(pcopy, fspec[i], sizeof(pcopy));  
   
                 /*  
                  * get rid of any trailing slashes.  
                  */  
                 STRIP_SLASH(pcopy);  
   
                 /*  
                  * Load the information.  
                  */  
                 cf = cvs_file_loadinfo(pcopy, flags, cb, arg, freecf);  
                 if (cf == NULL) {  
                         if (cvs_error != CVS_EX_OK)  
                                 return (-1);  
                         continue;  
                 }  
   
                 /*  
                  * If extra actions are needed, do them now.  
                  */  
                 if (cf->cf_type == DT_DIR) {  
                         /* do possible extra actions .. */  
                 } else {  
                         /* do possible extra actions .. */  
                 }  
   
                 /*  
                  * Attach it to a list if requested, otherwise  
                  * just free it again.  
                  */  
                 if (list != NULL)  
                         SIMPLEQ_INSERT_TAIL(list, cf, cf_list);  
                 else  
                         cvs_file_free(cf);  
         }  
   
         return (0);  
 }  }
   
 /*  void
  * Load the neccesary information about file or directory <path>.  cvs_file_walklist(struct cvs_flisthead *fl, struct cvs_recursion *cr)
  * Returns a pointer to the loaded information on success, or NULL  
  * on failure.  
  *  
  * If cb is not NULL, the requested path will be passed to that callback  
  * with <arg> as an argument.  
  *  
  * the <freecf> argument is passed to cvs_file_getdir, if this is 1  
  * CVSFILE * structs will be free'd once we are done with them.  
  */  
 CVSFILE *  
 cvs_file_loadinfo(char *path, int flags, int (*cb)(CVSFILE *, void *),  
     void *arg, int freecf)  
 {  {
         CVSFILE *cf, *base;          int len, fd, type;
         CVSENTRIES *entf;  
         struct cvs_ent *ent;  
         char *p;  
         char parent[MAXPATHLEN], item[MAXPATHLEN];  
         int type, callit;  
         struct stat st;          struct stat st;
         struct cvsroot *root;          struct cvs_file *cf;
           struct cvs_filelist *l, *nxt;
           char *d, *f, *repo, *fpath;
   
         type = 0;          fpath = xmalloc(MAXPATHLEN);
         base = cf = NULL;          repo = xmalloc(MAXPATHLEN);
         entf = NULL;  
         ent = NULL;  
   
         /*          for (l = TAILQ_FIRST(fl); l != NULL; l = nxt) {
          * We first have to find out what type of item we are                  if (cvs_quit)
          * dealing with. A file or a directory.                          fatal("received signal %d", sig_received);
          *  
          * We can do this by stat(2)'ing the item, but since it  
          * might be gone we also check the Entries file in the  
          * parent directory.  
          */  
   
         /* get parent directory */                  cvs_log(LP_TRACE, "cvs_file_walklist: element '%s'",
         if ((p = strrchr(path, '/')) != NULL) {                      l->file_path);
                 *p++ = '\0';  
                 strlcpy(parent, path, sizeof(parent));  
                 strlcpy(item, p, sizeof(item));  
                 *--p = '/';  
         } else {  
                 strlcpy(parent, ".", sizeof(parent));  
                 strlcpy(item, path, sizeof(item));  
         }  
   
         /*                  if ((f = basename(l->file_path)) == NULL)
          * There might not be an Entries file, so do not fail if there                          fatal("cvs_file_walklist: basename failed");
          * is none available to get the info from.                  if ((d = dirname(l->file_path)) == NULL)
          */                          fatal("cvs_file_walklist: dirname failed");
         entf = cvs_ent_open(parent, O_RDWR);  
   
         /*                  if ((fd = open(l->file_path, O_RDONLY)) != -1) {
          * Load the Entry if we successfully opened the Entries file.                          if (fstat(fd, &st) == -1) {
          */                                  cvs_log(LP_ERRNO, "%s", l->file_path);
         if (entf != NULL)                                  (void)close(fd);
                 ent = cvs_ent_get(entf, item);                                  goto next;
                           }
   
         /*                          if (S_ISDIR(st.st_mode))
          * No Entry available? fall back to stat(2)'ing the item, if                                  type = CVS_DIR;
          * that fails, assume a normal file.                          else if (S_ISREG(st.st_mode))
          */                                  type = CVS_FILE;
         if (ent == NULL) {                          else {
                 if (stat(path, &st) == -1)                                  cvs_log(LP_ERR,
                         type = DT_REG;                                      "ignoring bad file type for %s",
                 else                                      l->file_path);
                         type = IFTODT(st.st_mode);                                  (void)close(fd);
         } else {                                  goto next;
                 if (ent->ce_type == CVS_ENT_DIR)                          }
                         type = DT_DIR;                  } else {
                 else                          if (stat(d, &st) == -1) {
                         type = DT_REG;                                  cvs_log(LP_ERRNO, "%s", d);
         }                                  goto next;
                           }
   
         /*                          cvs_get_repo(d, repo, MAXPATHLEN);
          * Get the base, which is <parent> for a normal file or                          len = snprintf(fpath, MAXPATHLEN, "%s/%s",
          * <path> for a directory.                              repo, f);
          */                          if (len == -1 || len >= MAXPATHLEN)
         if (type == DT_DIR)                                  fatal("cvs_file_walklist: overflow");
                 base = cvs_file_lget(path, flags, NULL, entf, ent);  
         else  
                 base = cvs_file_lget(parent, flags, NULL, entf, NULL);  
   
         if (base == NULL) {                          if ((fd = open(fpath, O_RDONLY)) == -1) {
                 cvs_log(LP_ERR, "failed to obtain directory info for '%s'",                                  strlcat(fpath, RCS_FILE_EXT, MAXPATHLEN);
                     parent);                                  fd = open(fpath, O_RDONLY);
                 cvs_error = CVS_EX_FILE;                          }
                 goto fail;  
         }  
   
         /*                          if (fd != -1) {
          * Sanity.                                  if (fstat(fd, &st) == -1)
          */                                          fatal("cvs_file_walklist: %s: %s",
         if (base->cf_type != DT_DIR) {                                               fpath, strerror(errno));
                 cvs_log(LP_ERR, "base directory isn't a directory at all");  
                 goto fail;  
         }  
   
         root = CVS_DIR_ROOT(base);                                  if (S_ISDIR(st.st_mode))
         if (root == NULL) {                                          type = CVS_DIR;
                 cvs_error = CVS_EX_BADROOT;                                  else if (S_ISREG(st.st_mode))
                 goto fail;                                          type = CVS_FILE;
         }                                  else {
                                           cvs_log(LP_ERR,
         /*                                              "ignoring bad file type for %s",
          * If we have a normal file, get the info and link it                                              l->file_path);
          * to the base.                                          (void)close(fd);
          */                                          goto next;
         if (type != DT_DIR) {                                  }
                 cf = cvs_file_lget(path, flags, base, entf, ent);  
                 if (cf == NULL) {                                  /* this file is not in our working copy yet */
                         cvs_error = CVS_EX_DATA;                                  (void)close(fd);
                         goto fail;                                  fd = -1;
                           }
                 }                  }
   
                 cvs_file_attach(base, cf);                  cf = cvs_file_get_cf(d, f, fd, type);
         }                  if (cf->file_type == CVS_DIR) {
                           cvs_file_walkdir(cf, cr);
         /*                  } else {
          * Always pass the base directory, unless:                          if (cr->local != NULL)
          * - we are running in server or local mode and the path is not "."                                  cr->local(cf);
          * - the directory does not exist on disk.  
          * - the callback is NULL.  
         */  
         callit = 1;  
         if (cb == NULL)  
                 callit = 0;  
   
         if (cvs_cmdop == CVS_OP_SERVER && type != DT_DIR)  
                 callit = 0;  
   
         if (root->cr_method == CVS_METHOD_LOCAL && type != DT_DIR)  
                 callit = 0;  
   
         if (!(base->cf_flags & CVS_FILE_ONDISK))  
                 callit = 0;  
   
         if (callit != 0) {  
                 if ((cvs_error = cb(base,arg)) != CVS_EX_OK)  
                         goto fail;  
         }  
   
         /*  
          * If we have a normal file, pass it as well.  
          */  
         if (type != DT_DIR) {  
                 if (cb != NULL && (cvs_error = cb(cf, arg)) != CVS_EX_OK)  
                         goto fail;  
         } else {  
                 /*  
                  * If the directory exists, recurse through it.  
                  */  
                 if ((base->cf_flags & CVS_FILE_ONDISK) &&  
                     cvs_file_getdir(base, flags, cb, arg, freecf) < 0) {  
                         cvs_error = CVS_EX_FILE;  
                         goto fail;  
                 }                  }
         }  
   
         if (entf != NULL) {                  cvs_file_free(cf);
                 cvs_ent_close(entf);  
                 entf = NULL;  
         }  
   
         return (base);  next:
                   nxt = TAILQ_NEXT(l, flist);
                   TAILQ_REMOVE(fl, l, flist);
   
 fail:                  xfree(l->file_path);
         if (entf != NULL)                  xfree(l);
                 cvs_ent_close(entf);  
         if (base != NULL)  
                 cvs_file_free(base);  
         return (NULL);  
 }  
   
 /*  
  * cvs_file_find()  
  *  
  * Find the pointer to a CVS file entry within the file hierarchy <hier>.  
  * The file's pathname <path> must be relative to the base of <hier>.  
  * Returns the entry on success, or NULL on failure.  
  */  
 CVSFILE *  
 cvs_file_find(CVSFILE *hier, const char *path)  
 {  
         char *pp, *sp, pbuf[MAXPATHLEN];  
         CVSFILE *sf, *cf;  
   
         strlcpy(pbuf, path, sizeof(pbuf));  
   
         cf = hier;  
         pp = pbuf;  
         do {  
                 sp = strchr(pp, '/');  
                 if (sp != NULL)  
                         *(sp++) = '\0';  
   
                 /* special case */  
                 if (*pp == '.') {  
                         if (*(pp + 1) == '.' && *(pp + 2) == '\0') {  
                                 /* request to go back to parent */  
                                 if (cf->cf_parent == NULL) {  
                                         cvs_log(LP_NOTICE,  
                                             "path %s goes back too far", path);  
                                         return (NULL);  
                                 }  
                                 cf = cf->cf_parent;  
                                 continue;  
                         } else if (*(pp + 1) == '\0')  
                                 continue;  
                 }  
   
                 SIMPLEQ_FOREACH(sf, &(cf->cf_files), cf_list)  
                         if (cvs_file_cmpname(pp, sf->cf_name) == 0)  
                                 break;  
                 if (sf == NULL)  
                         return (NULL);  
   
                 cf = sf;  
                 pp = sp;  
         } while (sp != NULL);  
   
         return (cf);  
 }  
   
   
 /*  
  * cvs_file_getpath()  
  *  
  * Get the full path of the file <file> and store it in <buf>, which is of  
  * size <len>.  For portability, it is recommended that <buf> always be  
  * at least MAXPATHLEN bytes long.  
  * Returns a pointer to the start of the path.  
  */  
 char *  
 cvs_file_getpath(CVSFILE *file, char *buf, size_t len)  
 {  
         memset(buf, '\0', len);  
         if (file->cf_dir != NULL) {  
                 strlcat(buf, file->cf_dir, len);  
                 strlcat(buf, "/", len);  
         }          }
   
         strlcat(buf, file->cf_name, len);          xfree(fpath);
         return (buf);          xfree(repo);
 }  }
   
 /*  void
  * cvs_file_attach()  cvs_file_walkdir(struct cvs_file *cf, struct cvs_recursion *cr)
  *  
  * Attach the file <file> as one of the children of parent <parent>, which  
  * has to be a file of type DT_DIR.  
  * Returns 0 on success, or -1 on failure.  
  */  
 int  
 cvs_file_attach(CVSFILE *parent, CVSFILE *file)  
 {  {
         if (parent->cf_type != DT_DIR)          int l;
                 return (-1);          FILE *fp;
           int nbytes;
           size_t len;
           long base;
           size_t bufsize;
           struct stat st;
           struct dirent *dp;
           struct cvs_ent *ent;
           struct cvs_ignpat *ip;
           struct cvs_ent_line *line;
           struct cvs_flisthead fl, dl;
           CVSENTRIES *entlist;
           char *buf, *ebuf, *cp, *repo, *fpath;
   
         SIMPLEQ_INSERT_TAIL(&(parent->cf_files), file, cf_list);          cvs_log(LP_TRACE, "cvs_file_walkdir(%s)", cf->file_path);
         file->cf_parent = parent;  
   
         return (0);          if (cr->enterdir != NULL)
 }                  cr->enterdir(cf);
   
           if (cr->local != NULL)
                   cr->local(cf);
   
 /*          repo = xmalloc(MAXPATHLEN);
  * Load directory information          fpath = xmalloc(MAXPATHLEN);
  */  
 static int  
 cvs_load_dirinfo(CVSFILE *cf, int flags)  
 {  
         char fpath[MAXPATHLEN];  
         char pbuf[MAXPATHLEN];  
         struct stat st;  
         int l;  
   
         cvs_file_getpath(cf, fpath, sizeof(fpath));  
   
         /*          /*
          * Try to obtain the Root for this given directory, if we cannot           * If we do not have a admin directory inside here, dont bother.
          * get it, fail, unless we are dealing with a directory that is  
          * unknown or not on disk.  
          */           */
         cf->cf_root = cvsroot_get(fpath);          l = snprintf(fpath, MAXPATHLEN, "%s/%s", cf->file_path,
         if (cf->cf_root == NULL) {              CVS_PATH_CVSDIR);
                 if (cf->cf_cvstat == CVS_FST_UNKNOWN ||          if (l == -1 || l >= MAXPATHLEN)
                     !(cf->cf_flags & CVS_FILE_ONDISK))                  fatal("cvs_file_walkdir: overflow");
                         return (0);  
                 return (-1);  
         }  
   
         /* if the CVS administrative directory exists, load the info */          if (stat(fpath, &st) == -1) {
         l = snprintf(pbuf, sizeof(pbuf), "%s/" CVS_PATH_CVSDIR, fpath);                  xfree(repo);
         if (l == -1 || l >= (int)sizeof(pbuf)) {                  xfree(fpath);
                 errno = ENAMETOOLONG;                  return;
                 cvs_log(LP_ERRNO, "%s", pbuf);  
                 return (-1);  
         }          }
   
         if (stat(pbuf, &st) == 0 && S_ISDIR(st.st_mode)) {  
                 if (cvs_readrepo(fpath, pbuf, sizeof(pbuf)) == 0)  
                         cf->cf_repo = xstrdup(pbuf);  
         } else {  
                 /*  
                  * Fill in the repo path ourselfs.  
                  */  
                 if (cvs_repo_base != NULL) {  
                         l = snprintf(pbuf, sizeof(pbuf), "%s/%s",  
                             cvs_repo_base, fpath);  
                         if (l == -1 || l >= (int)sizeof(pbuf))  
                                 return (-1);  
   
                         cf->cf_repo = xstrdup(pbuf);  
                 } else  
                         cf->cf_repo = NULL;  
         }  
   
         return (0);  
 }  
   
 /*  
  * cvs_file_getdir()  
  *  
  * Get a cvs directory structure for the directory whose path is <dir>.  
  * This function should not free the directory information on error, as this  
  * is performed by cvs_file_free().  
  */  
 static int  
 cvs_file_getdir(CVSFILE *cf, int flags, int (*cb)(CVSFILE *, void *),  
     void *arg, int freecf)  
 {  
         int ret;  
         size_t len;  
         DIR *dp;  
         struct dirent *de;  
         char fpath[MAXPATHLEN], pbuf[MAXPATHLEN];  
         CVSENTRIES *entf;  
         CVSFILE *cfp;  
         struct cvs_ent *ent;  
         struct cvs_flist dirs;  
         int nfiles, ndirs;  
   
         if ((flags & CF_KNOWN) && cf->cf_cvstat == CVS_FST_UNKNOWN)  
                 return (0);  
   
         /*          /*
          * if we are working with a repository, fiddle with           * check for a local .cvsignore file
          * the pathname again.  
          */           */
         if (flags & CF_REPO) {          l = snprintf(fpath, MAXPATHLEN, "%s/.cvsignore", cf->file_path);
                 ret = snprintf(fpath, sizeof(fpath), "%s%s%s",          if (l == -1 || l >= MAXPATHLEN)
                     cf->cf_root->cr_dir,                  fatal("cvs_file_walkdir: overflow");
                     (cf->cf_dir != NULL) ? "/" : "",  
                     (cf->cf_dir != NULL) ? cf->cf_dir : "");  
                 if (ret == -1 || ret >= (int)sizeof(fpath))  
                         return (-1);  
   
                 if (cf->cf_dir != NULL)          if ((fp = fopen(fpath, "r")) != NULL) {
                         xfree(cf->cf_dir);                  while (fgets(fpath, MAXPATHLEN, fp)) {
                 cf->cf_dir = xstrdup(fpath);                          len = strlen(fpath);
         }                          if (fpath[len - 1] == '\n')
                                   fpath[len - 1] = '\0';
   
         nfiles = ndirs = 0;                          cvs_file_ignore(fpath, &dir_ign_pats);
         SIMPLEQ_INIT(&dirs);                  }
         cvs_file_getpath(cf, fpath, sizeof(fpath));  
   
         if ((dp = opendir(fpath)) == NULL) {                  (void)fclose(fp);
                 cvs_log(LP_ERRNO, "failed to open directory '%s'", fpath);  
                 return (-1);  
         }          }
   
         ret = -1;          if (fstat(cf->fd, &st) == -1)
         entf = cvs_ent_open(fpath, O_RDWR);                  fatal("cvs_file_walkdir: %s %s", cf->file_path,
         while ((de = readdir(dp)) != NULL) {                      strerror(errno));
                 if (!strcmp(de->d_name, ".") ||  
                     !strcmp(de->d_name, ".."))  
                         continue;  
   
                 len = cvs_path_cat(fpath, de->d_name, pbuf, sizeof(pbuf));          bufsize = st.st_size;
                 if (len >= sizeof(pbuf))          if (bufsize < st.st_blksize)
                         goto done;                  bufsize = st.st_blksize;
   
                 if (entf != NULL)          buf = xmalloc(bufsize);
                         ent = cvs_ent_get(entf, de->d_name);          TAILQ_INIT(&fl);
                 else          TAILQ_INIT(&dl);
                         ent = NULL;  
   
                 /*          while ((nbytes = getdirentries(cf->fd, buf, bufsize, &base)) > 0) {
                  * Do some filtering on the current directory item.                  ebuf = buf + nbytes;
                  */                  cp = buf;
                 if ((flags & CF_IGNORE) && cvs_file_chkign(de->d_name))  
                         continue;  
   
                 if (!(flags & CF_RECURSE) && (de->d_type == DT_DIR)) {                  while (cp < ebuf) {
                         if (ent != NULL)                          dp = (struct dirent *)cp;
                                 ent->processed = 1;                          if (!strcmp(dp->d_name, ".") ||
                         continue;                              !strcmp(dp->d_name, "..") ||
                 }                              !strcmp(dp->d_name, CVS_PATH_CVSDIR) ||
                               dp->d_reclen == 0) {
                                   cp += dp->d_reclen;
                                   continue;
                           }
   
                 if (de->d_type != DT_DIR && (flags & CF_NOFILES))                          if (cvs_file_chkign(dp->d_name)) {
                         continue;                                  cp += dp->d_reclen;
                                   continue;
                           }
   
                 cfp = cvs_file_lget(pbuf, flags, cf, entf, ent);                          l = snprintf(fpath, MAXPATHLEN, "%s/%s",
                 if (cfp == NULL) {                              cf->file_path, dp->d_name);
                         cvs_log(LP_ERR, "failed to get '%s'", pbuf);                          if (l == -1 || l >= MAXPATHLEN)
                         goto done;                                  fatal("cvs_file_walkdir: overflow");
                 }  
   
                 /*                          /*
                  * A file is linked to the parent <cf>, a directory                           * Anticipate the file type to sort them,
                  * is added to the dirs SIMPLEQ list for later use.                           * note that we do not determine the final
                  */                           * type until we actually have the fd floating
                 if (cfp->cf_type != DT_DIR && !freecf) {                           * around.
                         SIMPLEQ_INSERT_TAIL(&(cf->cf_files), cfp, cf_list);                           */
                         nfiles++;                          if (dp->d_type == DT_DIR)
                 } else if (cfp->cf_type == DT_DIR) {                                  cvs_file_get(fpath, &dl);
                         SIMPLEQ_INSERT_TAIL(&dirs, cfp, cf_list);                          else if (dp->d_type == DT_REG)
                         ndirs++;                                  cvs_file_get(fpath, &fl);
                 }  
   
                 /*                          cp += dp->d_reclen;
                  * Now, for a file, pass it to the callback if it was  
                  * supplied to us.  
                  */  
                 if (cfp->cf_type != DT_DIR && cb != NULL) {  
                         if ((cvs_error = cb(cfp, arg)) != CVS_EX_OK)  
                                 goto done;  
                 }                  }
   
                 /*  
                  * Mark the entry as processed.  
                  */  
                 if (ent != NULL)  
                         ent->processed = 1;  
   
                 /*  
                  * If we don't want to keep it, free it  
                  */  
                 if (cfp->cf_type != DT_DIR && freecf)  
                         cvs_file_free(cfp);  
         }          }
   
         closedir(dp);          if (nbytes == -1)
         dp = NULL;                  fatal("cvs_file_walkdir: %s %s", cf->file_path,
                       strerror(errno));
   
         /*          xfree(buf);
          * Pass over all of the entries now, so we pickup any files  
          * that might have been lost, or are for some reason not on disk.  
          *  
          * (Follows the same procedure as above ... can we merge them?)  
          */  
         while (entf != NULL && (ent = cvs_ent_next(entf)) != NULL) {  
                 if (ent->processed == 1)  
                         continue;  
                 if (!(flags & CF_RECURSE) && ent->ce_type == CVS_ENT_DIR)  
                         continue;  
                 if ((flags & CF_NOFILES) && ent->ce_type != CVS_ENT_DIR)  
                         continue;  
   
                 len = cvs_path_cat(fpath, ent->ce_name, pbuf, sizeof(pbuf));          while ((ip = TAILQ_FIRST(&dir_ign_pats)) != NULL) {
                 if (len >= sizeof(pbuf))                  TAILQ_REMOVE(&dir_ign_pats, ip, ip_list);
                         goto done;                  xfree(ip);
           }
   
                 cfp = cvs_file_lget(pbuf, flags, cf, entf, ent);          entlist = cvs_ent_open(cf->file_path);
                 if (cfp == NULL) {          TAILQ_FOREACH(line, &(entlist->cef_ent), entries_list) {
                         cvs_log(LP_ERR, "failed to fetch '%s'", pbuf);                  ent = cvs_ent_parse(line->buf);
                         goto done;  
                 }  
   
                 if (cfp->cf_type != DT_DIR && !freecf) {                  l = snprintf(fpath, MAXPATHLEN, "%s/%s", cf->file_path,
                         SIMPLEQ_INSERT_TAIL(&(cf->cf_files), cfp, cf_list);                      ent->ce_name);
                         nfiles++;                  if (l == -1 || l >= MAXPATHLEN)
                 } else if (cfp->cf_type == DT_DIR) {                          fatal("cvs_file_walkdir: overflow");
                         SIMPLEQ_INSERT_TAIL(&dirs, cfp, cf_list);  
                         ndirs++;  
                 }  
   
                 if (cfp->cf_type != DT_DIR && cb != NULL) {                  if (ent->ce_type == CVS_ENT_DIR)
                         if ((cvs_error = cb(cfp, arg)) != CVS_EX_OK)                          cvs_file_get(fpath, &dl);
                                 goto done;                  else if (ent->ce_type == CVS_ENT_FILE)
                 }                          cvs_file_get(fpath, &fl);
   
                 if (cfp->cf_type != DT_DIR && freecf)                  cvs_ent_free(ent);
                         cvs_file_free(cfp);  
         }          }
   
         /*          cvs_ent_close(entlist, ENT_NOSYNC);
          * Sort files and dirs if requested.  
          */  
         if (flags & CF_SORT) {  
                 if (nfiles > 0)  
                         cvs_file_sort(&(cf->cf_files), nfiles);  
                 if (ndirs > 0)  
                         cvs_file_sort(&dirs, ndirs);  
         }  
   
         /*          cvs_get_repo(cf->file_path, repo, MAXPATHLEN);
          * Finally, run over the directories we have encountered.          cvs_repository_lock(repo);
          * Before calling cvs_file_getdir() on them, we pass them  
          * to the callback first.  
          */  
         while (!SIMPLEQ_EMPTY(&dirs)) {  
                 cfp = SIMPLEQ_FIRST(&dirs);  
                 SIMPLEQ_REMOVE_HEAD(&dirs, cf_list);  
   
                 if (!freecf)          cvs_repository_getdir(repo, cf->file_path, &fl, &dl);
                         SIMPLEQ_INSERT_TAIL(&(cf->cf_files), cfp, cf_list);  
   
                 if (cb != NULL) {          cvs_file_walklist(&fl, cr);
                         if ((cvs_error = cb(cfp, arg)) != CVS_EX_OK)          cvs_file_freelist(&fl);
                                 goto done;  
                 }  
   
                 if ((cfp->cf_flags & CVS_FILE_ONDISK) &&          cvs_repository_unlock(repo);
                     cvs_file_getdir(cfp, flags, cb, arg, freecf) < 0)  
                         goto done;  
   
                 if (freecf)          cvs_file_walklist(&dl, cr);
                         cvs_file_free(cfp);          cvs_file_freelist(&dl);
         }  
   
         ret = 0;          xfree(repo);
         cfp = NULL;          xfree(fpath);
 done:  
         if (cfp != NULL && freecf)  
                 cvs_file_free(cfp);  
   
         while (!SIMPLEQ_EMPTY(&dirs)) {          if (cr->leavedir != NULL)
                 cfp = SIMPLEQ_FIRST(&dirs);                  cr->leavedir(cf);
                 SIMPLEQ_REMOVE_HEAD(&dirs, cf_list);  
   
                 cvs_file_free(cfp);  
         }  
   
         if (entf != NULL)  
                 cvs_ent_close(entf);  
         if (dp != NULL)  
                 closedir(dp);  
   
         return (ret);  
 }  }
   
   
 /*  
  * cvs_file_free()  
  *  
  * Free a cvs_file structure and its contents.  
  */  
 void  void
 cvs_file_free(CVSFILE *cf)  cvs_file_freelist(struct cvs_flisthead *fl)
 {  {
         CVSFILE *child;          struct cvs_filelist *f;
   
         if (cf->cf_name != NULL)          while ((f = TAILQ_FIRST(fl)) != NULL) {
                 xfree(cf->cf_name);                  TAILQ_REMOVE(fl, f, flist);
                   xfree(f->file_path);
         if (cf->cf_dir != NULL)                  xfree(f);
                 xfree(cf->cf_dir);  
   
         if (cf->cf_type == DT_DIR) {  
                 if (cf->cf_root != NULL)  
                         cvsroot_remove(cf->cf_root);  
                 if (cf->cf_repo != NULL)  
                         xfree(cf->cf_repo);  
                 while (!SIMPLEQ_EMPTY(&(cf->cf_files))) {  
                         child = SIMPLEQ_FIRST(&(cf->cf_files));  
                         SIMPLEQ_REMOVE_HEAD(&(cf->cf_files), cf_list);  
                         cvs_file_free(child);  
                 }  
         } else {  
                 if (cf->cf_tag != NULL)  
                         xfree(cf->cf_tag);  
                 if (cf->cf_opts != NULL)  
                         xfree(cf->cf_opts);  
         }          }
   
         xfree(cf);  
 }  }
   
   void
 /*  cvs_file_classify(struct cvs_file *cf)
  * cvs_file_sort()  
  *  
  * Sort a list of cvs file structures according to their filename.  The list  
  * <flp> is modified according to the sorting algorithm.  The number of files  
  * in the list must be given by <nfiles>.  
  * Returns 0 on success, or -1 on failure.  
  */  
 static int  
 cvs_file_sort(struct cvs_flist *flp, u_int nfiles)  
 {  {
         int i;          size_t len;
         size_t nb;          time_t mtime;
         CVSFILE *cf, **cfvec;          struct stat st;
           int rflags, l, ismodified, rcsdead;
           CVSENTRIES *entlist = NULL;
           const char *state;
           char *repo, *rcsfile, r1[16], r2[16];
   
         cfvec = xcalloc((size_t)nfiles, sizeof(*cfvec));          cvs_log(LP_TRACE, "cvs_file_classify(%s)", cf->file_path);
   
         i = 0;          if (!strcmp(cf->file_path, ".")) {
         SIMPLEQ_FOREACH(cf, flp, cf_list) {                  cf->file_status = FILE_UPTODATE;
                 if (i == (int)nfiles) {                  return;
                         cvs_log(LP_WARN, "too many files to sort");  
                         /* rebuild the list and abort sorting */  
                         while (--i >= 0)  
                                 SIMPLEQ_INSERT_HEAD(flp, cfvec[i], cf_list);  
                         xfree(cfvec);  
                         return (-1);  
                 }  
                 cfvec[i++] = cf;  
   
                 /* now unlink it from the list,  
                  * we'll put it back in order later  
                  */  
                 SIMPLEQ_REMOVE_HEAD(flp, cf_list);  
         }          }
   
         /* clear the list just in case */          entlist = cvs_ent_open(cf->file_wd);
         SIMPLEQ_INIT(flp);  
         nb = (size_t)i;  
   
         heapsort(cfvec, nb, sizeof(cf), cvs_file_cmp);          repo = xmalloc(MAXPATHLEN);
           rcsfile = xmalloc(MAXPATHLEN);
   
         /* rebuild the list from the bottom up */          cvs_get_repo(cf->file_wd, repo, MAXPATHLEN);
         for (i = (int)nb - 1; i >= 0; i--)          l = snprintf(rcsfile, MAXPATHLEN, "%s/%s",
                 SIMPLEQ_INSERT_HEAD(flp, cfvec[i], cf_list);              repo, cf->file_name);
           if (l == -1 || l >= MAXPATHLEN)
                   fatal("cvs_file_classify: overflow");
   
         xfree(cfvec);          if (cf->file_type == CVS_FILE) {
         return (0);                  len = strlcat(rcsfile, RCS_FILE_EXT, MAXPATHLEN);
 }                  if (len >= MAXPATHLEN)
                           fatal("cvs_file_classify: truncation");
           }
   
           cf->file_rpath = xstrdup(rcsfile);
           cf->file_ent = cvs_ent_get(entlist, cf->file_name);
   
 static int          if (cf->file_type == CVS_DIR) {
 cvs_file_cmp(const void *f1, const void *f2)                  if (cf->fd == -1 && stat(rcsfile, &st) != -1)
 {                          cf->file_status = DIR_CREATE;
         const CVSFILE *cf1, *cf2;                  else if (cf->file_ent != NULL)
         cf1 = *(CVSFILE * const *)f1;                          cf->file_status = FILE_UPTODATE;
         cf2 = *(CVSFILE * const *)f2;                  xfree(repo);
         return cvs_file_cmpname(cf1->cf_name, cf2->cf_name);                  xfree(rcsfile);
 }                  cvs_ent_close(entlist, ENT_NOSYNC);
                   return;
           }
   
           rflags = 0;
           cf->repo_fd = open(cf->file_rpath, O_RDONLY);
           if (cf->repo_fd != -1) {
                   cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, rflags);
                   if (cf->file_rcs == NULL)
                           fatal("cvs_file_classify: rcs_open failed while it "
                               "shouldn't");
           } else {
                   cf->file_rcs = NULL;
           }
   
 /*          if (cf->file_ent != NULL)
  * cvs_file_alloc()                  rcsnum_tostr(cf->file_ent->ce_rev, r1, sizeof(r1));
  *          if (cf->file_rcs != NULL)
  * Allocate a CVSFILE structure and initialize its internals.                  rcsnum_tostr(cf->file_rcs->rf_head, r2, sizeof(r2));
  */  
 CVSFILE *  
 cvs_file_alloc(const char *path, u_int type)  
 {  
         CVSFILE *cfp;  
         char *p;  
   
         cfp = xcalloc(1, sizeof(*cfp));          ismodified = rcsdead = 0;
           if (cf->fd != -1 && cf->file_ent != NULL) {
                   if (fstat(cf->fd, &st) == -1)
                           fatal("cvs_file_classify: %s", strerror(errno));
   
         cfp->cf_type = type;                  mtime = cvs_hack_time(st.st_mtime, 1);
         cfp->cf_cvstat = CVS_FST_UNKNOWN;                  if (mtime == 0)
                           fatal("to gmt failed");
   
         if (type == DT_DIR) {                  if (mtime != cf->file_ent->ce_mtime)
                 SIMPLEQ_INIT(&(cfp->cf_files));                          ismodified = 1;
         }          }
   
         cfp->cf_name = xstrdup(basename(path));          if (cf->file_rcs != NULL) {
         if ((p = strrchr(path, '/')) != NULL) {                  state = rcs_state_get(cf->file_rcs, cf->file_rcs->rf_head);
                 *p = '\0';                  if (state == NULL)
                 if (strcmp(path, "."))                          fatal("failed to get state for HEAD for %s",
                         cfp->cf_dir = xstrdup(path);                              cf->file_path);
                 else                  if (!strcmp(state, "dead"))
                         cfp->cf_dir = NULL;                          rcsdead = 1;
                 *p = '/';  
         } else  
                 cfp->cf_dir = NULL;  
   
         return (cfp);  
 }  
   
   
 /*  
  * cvs_file_lget()  
  *  
  * Get the file and link it with the parent right away.  
  * Returns a pointer to the created file structure on success, or NULL on  
  * failure.  
  */  
 static CVSFILE *  
 cvs_file_lget(const char *path, int flags, CVSFILE *parent, CVSENTRIES *pent,  
     struct cvs_ent *ent)  
 {  
         char *c;  
         int ret;  
         u_int type;  
         struct stat st;  
         CVSFILE *cfp;  
         struct cvsroot *root;  
   
         type = DT_UNKNOWN;  
         ret = stat(path, &st);  
         if (ret == 0)  
                 type = IFTODT(st.st_mode);  
   
         if ((flags & CF_REPO) && type != DT_DIR) {  
                 if ((c = strrchr(path, ',')) == NULL)  
                         return (NULL);  
                 *c = '\0';  
         }          }
   
         if ((cfp = cvs_file_alloc(path, type)) == NULL)          /*
                 return (NULL);           * 10 Sin
         cfp->cf_parent = parent;           * 20 Goto hell
         cfp->cf_entry = pent;           * (I welcome you if-else hell)
            */
         if (cfp->cf_type == DT_DIR && cfp->cf_parent == NULL)          if (cf->file_ent == NULL) {
                 cfp->cf_flags |= CVS_DIRF_BASE;                  if (cf->file_rcs == NULL) {
                           if (cf->fd == -1) {
         if (ret == 0) {                                  if (verbosity > 1)
                 cfp->cf_mode = st.st_mode & ACCESSPERMS;                                          cvs_log(LP_NOTICE,
                 if (cfp->cf_type == DT_REG)                                              "nothing known about '%s'",
                         cfp->cf_mtime = st.st_mtime;                                              cf->file_path);
                 cfp->cf_flags |= CVS_FILE_ONDISK;                          } else {
                                   if (verbosity > 1)
                 if (ent == NULL)                                          cvs_log(LP_NOTICE,
                         if (cfp->cf_flags & CVS_DIRF_BASE)                                              "use add to create an entry for %s",
                                 cfp->cf_cvstat = CVS_FST_UPTODATE;                                              cf->file_path);
                         else  
                                 cfp->cf_cvstat = CVS_FST_UNKNOWN;  
                 else {  
                         /* always show directories as up-to-date */  
                         if (ent->ce_type == CVS_ENT_DIR)  
                                 cfp->cf_cvstat = CVS_FST_UPTODATE;  
                         else if (rcsnum_cmp(ent->ce_rev, cvs_addedrev, 2) == 0)  
                                 cfp->cf_cvstat = CVS_FST_ADDED;  
                         else {  
                                 /*  
                                  * correct st.st_mtime first  
                                  */  
                                 if ((st.st_mtime =  
                                     cvs_hack_time(st.st_mtime, 1)) == 0) {  
                                         cvs_file_free(cfp);  
                                         return (NULL);  
                                 }  
   
                                 /* check last modified time */  
                                 if (ent->ce_mtime == (time_t)st.st_mtime) {  
                                         cfp->cf_cvstat = CVS_FST_UPTODATE;  
                                 } else {  
                                         cfp->cf_cvstat = CVS_FST_MODIFIED;  
                                 }  
                         }                          }
   
                         cfp->cf_etime = ent->ce_mtime;                          cf->file_status = FILE_UNKNOWN;
                   } else if (rcsdead == 1) {
                           if (cf->fd == -1) {
                                   cf->file_status = FILE_UPTODATE;
                           } else {
                                   cvs_log(LP_NOTICE,
                                       "use add to create an entry for %s",
                                       cf->file_path);
                                   cf->file_status = FILE_UNKNOWN;
                           }
                   } else {
                           cf->file_status = FILE_CHECKOUT;
                 }                  }
         } else {          } else if (cf->file_ent->ce_status == CVS_ENT_ADDED) {
                 if (ent == NULL) {                  if (cf->fd == -1) {
                         /* assume it is a file and unknown */                          if (verbosity > 1)
                         cfp->cf_cvstat = CVS_FST_UNKNOWN;                                  cvs_log(LP_NOTICE,
                         cfp->cf_type = DT_REG;                                      "warning: new-born %s has dissapeared",
                                       cf->file_path);
                           cf->file_status = FILE_REMOVE_ENTRY;
                   } else if (cf->file_rcs == NULL || rcsdead == 1) {
                           cf->file_status = FILE_ADDED;
                 } else {                  } else {
                         if (ent->ce_type == CVS_ENT_FILE)                          if (verbosity > 1)
                                 cfp->cf_type = DT_REG;                                  cvs_log(LP_NOTICE,
                         else if (ent->ce_type == CVS_ENT_DIR)                                      "conflict: %s already created by others",
                                 cfp->cf_type = DT_DIR;                                      cf->file_path);
                         else                          cf->file_status = FILE_CONFLICT;
                                 cvs_log(LP_WARN, "unknown ce_type %d",  
                                     ent->ce_type);  
   
                         if (ent->ce_status == CVS_ENT_REMOVED)  
                                 cfp->cf_cvstat = CVS_FST_REMOVED;  
                         else if (ent->ce_status == CVS_ENT_UPTODATE)  
                                 cfp->cf_cvstat = CVS_FST_UPTODATE;  
                         else if (ent->ce_status == CVS_ENT_ADDED)  
                                 cfp->cf_cvstat = CVS_FST_ADDED;  
                         else  
                                 cfp->cf_cvstat = CVS_FST_LOST;  
                 }                  }
           } else if (cf->file_ent->ce_status == CVS_ENT_REMOVED) {
                 /* XXX assume 0644 ? */                  if (cf->fd != -1) {
                 cfp->cf_mode = 0644;                          if (verbosity > 1)
         }                                  cvs_log(LP_NOTICE,
                                       "%s should be removed but is still there",
         if (ent != NULL) {                                      cf->file_path);
                 /* steal the RCSNUM */                          cf->file_status = FILE_REMOVED;
                 cfp->cf_lrev = ent->ce_rev;                  } else if (cf->file_rcs == NULL || rcsdead == 1) {
                           cf->file_status = FILE_REMOVE_ENTRY;
                 if (ent->ce_type == CVS_ENT_FILE) {                  } else {
                         if (ent->ce_tag[0] != '\0')                          if (strcmp(r1, r2)) {
                                 cfp->cf_tag = xstrdup(ent->ce_tag);                                  if (verbosity > 1)
                                           cvs_log(LP_NOTICE,
                         if (ent->ce_opts[0] != '\0')                                              "conflict: removed %s was modified"
                                 cfp->cf_opts = xstrdup(ent->ce_opts);                                              " by a second party",
                                               cf->file_path);
                                   cf->file_status = FILE_CONFLICT;
                           } else {
                                   cf->file_status = FILE_REMOVED;
                           }
                 }                  }
         }          } else if (cf->file_ent->ce_status == CVS_ENT_REG) {
                   if (cf->file_rcs == NULL || rcsdead == 1) {
                           if (cf->fd == -1) {
                                   if (verbosity > 1)
                                           cvs_log(LP_NOTICE,
                                               "warning: %s's entry exists but"
                                               " there is no longer a file"
                                               " in the repository,"
                                               " removing entry",
                                                cf->file_path);
                                   cf->file_status = FILE_REMOVE_ENTRY;
                           } else {
                                   if (ismodified) {
                                           if (verbosity > 1)
                                                   cvs_log(LP_NOTICE,
                                                       "conflict: %s is no longer "
                                                       "in the repository but is "
                                                       "locally modified",
                                                       cf->file_path);
                                           cf->file_status = FILE_CONFLICT;
                                   } else {
                                           if (verbosity > 1)
                                                   cvs_log(LP_NOTICE,
                                                       "%s is no longer in the "
                                                       "repository",
                                                       cf->file_path);
   
         if (cfp->cf_type == DT_DIR) {                                          cf->file_status = FILE_UNLINK;
                 if (cvs_load_dirinfo(cfp, flags) < 0) {                                  }
                         cvs_file_free(cfp);                          }
                         return (NULL);  
                 }  
         }  
   
         if (flags & CF_REPO) {  
                 root = CVS_DIR_ROOT(cfp);  
   
                 cfp->cf_mode = 0644;  
                 cfp->cf_cvstat = CVS_FST_LOST;  
   
                 c = xstrdup(cfp->cf_dir);  
                 xfree(cfp->cf_dir);  
   
                 if (strcmp(c, root->cr_dir)) {  
                         c += strlen(root->cr_dir) + 1;  
                         cfp->cf_dir = xstrdup(c);  
                         c -= strlen(root->cr_dir) + 1;  
                 } else {                  } else {
                         cfp->cf_dir = NULL;                          if (cf->fd == -1) {
                 }                                  if (verbosity > 1)
                                           cvs_log(LP_NOTICE,
                                               "warning: %s was lost",
                                               cf->file_path);
                                   cf->file_status = FILE_LOST;
                           } else {
                                   if (ismodified == 1)
                                           cf->file_status = FILE_MODIFIED;
                                   else
                                           cf->file_status = FILE_UPTODATE;
   
                 xfree(c);                                  if (strcmp(r1, r2)) {
                                           if (cf->file_status == FILE_MODIFIED)
                                                   cf->file_status = FILE_MERGE;
                                           else
                                                   cf->file_status = FILE_PATCH;
                                   }
                           }
                   }
         }          }
   
         if (cfp->cf_repo != NULL && cfp->cf_type == DT_DIR &&          xfree(repo);
             !strcmp(cfp->cf_repo, path))          xfree(rcsfile);
                 cfp->cf_cvstat = CVS_FST_UPTODATE;          cvs_ent_close(entlist, ENT_NOSYNC);
   }
   
         /*  void
          * In server mode, we do a few extra checks.  cvs_file_free(struct cvs_file *cf)
          */  {
         if (cvs_cmdop == CVS_OP_SERVER) {          xfree(cf->file_name);
                 /*          xfree(cf->file_wd);
                  * If for some reason a file was added,          xfree(cf->file_path);
                  * but does not exist anymore, start complaining.  
                  */  
                 if (!(cfp->cf_flags & CVS_FILE_ONDISK) &&  
                     (cfp->cf_cvstat == CVS_FST_ADDED) &&  
                     (cfp->cf_type != DT_DIR))  
                         cvs_log(LP_WARN, "new-born %s has disappeared", path);  
   
                 /*          if (cf->file_rpath != NULL)
                  * Any other needed checks?                  xfree(cf->file_rpath);
                  */          if (cf->file_ent != NULL)
         }                  cvs_ent_free(cf->file_ent);
           if (cf->file_rcs != NULL)
         return (cfp);                  rcs_close(cf->file_rcs);
           if (cf->fd != -1)
                   (void)close(cf->fd);
           if (cf->repo_fd != -1)
                   (void)close(cf->repo_fd);
           xfree(cf);
 }  }
   
   
 static int  static int
 cvs_file_cmpname(const char *name1, const char *name2)  cvs_file_cmpname(const char *name1, const char *name2)
 {  {
         return (cvs_nocase == 0) ? (strcmp(name1, name2)) :          return (cvs_nocase == 0) ? (strcmp(name1, name2)) :
             (strcasecmp(name1, name2));              (strcasecmp(name1, name2));
 }  
   
 /*  
  * remove any empty directories.  
  */  
 int  
 cvs_file_prune(char *path)  
 {  
         DIR *dirp;  
         int l, pwd, empty;  
         struct dirent *dp;  
         char fpath[MAXPATHLEN];  
         CVSENTRIES *entf;  
         CVSFILE *cfp;  
   
         pwd = (!strcmp(path, "."));  
   
         if ((dirp = opendir(path)) == NULL) {  
                 cvs_log(LP_ERRNO, "failed to open `%s'", fpath);  
                 return (-1);  
         }  
   
         empty = 0;  
         entf = cvs_ent_open(path, O_RDWR);  
   
         while ((dp = readdir(dirp)) != NULL) {  
                 if (!strcmp(dp->d_name, ".") ||  
                     !strcmp(dp->d_name, "..") ||  
                     !strcmp(dp->d_name, CVS_PATH_CVSDIR))  
                         continue;  
   
                 empty++;  
                 if (dp->d_type == DT_DIR) {  
                         l = snprintf(fpath, sizeof(fpath), "%s%s%s",  
                             (pwd) ? "" : path, (pwd) ? "" : "/", dp->d_name);  
                         if (l == -1 || l >= (int)sizeof(fpath)) {  
                                 errno = ENAMETOOLONG;  
                                 cvs_log(LP_ERRNO, "%s", fpath);  
                                 continue;  
                         }  
   
                         cfp = cvs_file_find(cvs_files, fpath);  
                         if (cfp == NULL)  
                                 continue;  
   
                         /* ignore unknown directories */  
                         if (cfp->cf_cvstat == CVS_FST_UNKNOWN)  
                                 continue;  
   
                         if (cvs_file_prune(fpath)) {  
                                 empty--;  
                                 if (entf)  
                                         cvs_ent_remove(entf, fpath, 0);  
                         } else {  
                                 empty++;  
                         }  
                 }  
         }  
   
         closedir(dirp);  
         if (entf)  
                 cvs_ent_close(entf);  
   
         empty = (empty == 0);  
         if (empty) {  
                 if (cvs_rmdir(path) < 0) {  
                         cvs_log(LP_ERR, "failed to prune `%s'", path);  
                         empty = 0;  
                 }  
         }  
   
         return (empty);  
 }  }

Legend:
Removed from v.1.137  
changed lines
  Added in v.1.138