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

Annotation of src/usr.bin/cvs/file.c, Revision 1.137

1.137   ! deraadt     1: /*     $OpenBSD: file.c,v 1.136 2006/04/05 01:38:55 ray Exp $  */
1.1       jfb         2: /*
                      3:  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.26      jfb         4:  * All rights reserved.
1.1       jfb         5:  *
1.26      jfb         6:  * Redistribution and use in source and binary forms, with or without
                      7:  * modification, are permitted provided that the following conditions
                      8:  * are met:
1.1       jfb         9:  *
1.26      jfb        10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
1.1       jfb        12:  * 2. The name of the author may not be used to endorse or promote products
1.26      jfb        13:  *    derived from this software without specific prior written permission.
1.1       jfb        14:  *
                     15:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
                     16:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
                     17:  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
                     18:  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
                     19:  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
                     20:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
                     21:  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     22:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
                     23:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.26      jfb        24:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1       jfb        25:  */
                     26:
1.134     xsa        27: #include "includes.h"
1.1       jfb        28:
                     29: #include "cvs.h"
1.83      xsa        30: #include "file.h"
1.1       jfb        31: #include "log.h"
                     32:
1.104     xsa        33: #define CVS_IGN_STATIC 0x01    /* pattern is static, no need to glob */
1.1       jfb        34:
1.104     xsa        35: #define CVS_CHAR_ISMETA(c)     ((c == '*') || (c == '?') || (c == '['))
1.1       jfb        36:
                     37: /* ignore pattern */
                     38: struct cvs_ignpat {
1.104     xsa        39:        char                            ip_pat[MAXNAMLEN];
                     40:        int                             ip_flags;
1.124     reyk       41:        TAILQ_ENTRY(cvs_ignpat)         ip_list;
1.1       jfb        42: };
                     43:
                     44: /*
                     45:  * Standard patterns to ignore.
                     46:  */
                     47: static const char *cvs_ign_std[] = {
                     48:        ".",
                     49:        "..",
                     50:        "*.o",
                     51:        "*.so",
1.45      xsa        52:        "*.a",
1.1       jfb        53:        "*.bak",
                     54:        "*.orig",
                     55:        "*.rej",
1.45      xsa        56:        "*.old",
1.1       jfb        57:        "*.exe",
                     58:        "*.depend",
1.45      xsa        59:        "*.obj",
                     60:        "*.elc",
                     61:        "*.ln",
                     62:        "*.olb",
1.1       jfb        63:        "CVS",
                     64:        "core",
1.102     joris      65:        "cvslog*",
1.49      jfb        66:        "*.core",
1.8       jfb        67:        ".#*",
1.45      xsa        68:        "*~",
                     69:        "_$*",
                     70:        "*$",
1.1       jfb        71: #ifdef OLD_SMELLY_CRUFT
                     72:        "RCSLOG",
                     73:        "tags",
                     74:        "TAGS",
                     75:        "RCS",
                     76:        "SCCS",
1.45      xsa        77:        "cvslog.*",     /* to ignore CVS_CLIENT_LOG output */
1.1       jfb        78:        "#*",
                     79:        ",*",
                     80: #endif
                     81: };
                     82:
1.11      jfb        83: /*
                     84:  * Entries in the CVS/Entries file with a revision of '0' have only been
                     85:  * added.  Compare against this revision to see if this is the case
                     86:  */
1.4       jfb        87: static RCSNUM *cvs_addedrev;
                     88:
1.104     xsa        89: TAILQ_HEAD(, cvs_ignpat)       cvs_ign_pats;
1.1       jfb        90:
1.100     joris      91: static int cvs_file_getdir(CVSFILE *, int, int (*)(CVSFILE *, void *),
                     92:     void *, int);
1.1       jfb        93:
1.104     xsa        94: static int      cvs_load_dirinfo(CVSFILE *, int);
                     95: static int      cvs_file_sort(struct cvs_flist *, u_int);
                     96: static int      cvs_file_cmp(const void *, const void *);
                     97: static int      cvs_file_cmpname(const char *, const char *);
                     98: static CVSFILE *cvs_file_alloc(const char *, u_int);
1.115     joris      99: static CVSFILE *cvs_file_lget(const char *, int, CVSFILE *, CVSENTRIES *,
                    100:                    struct cvs_ent *);
1.3       jfb       101:
1.1       jfb       102: /*
                    103:  * cvs_file_init()
                    104:  *
                    105:  */
                    106: int
                    107: cvs_file_init(void)
                    108: {
1.61      xsa       109:        int i, l;
1.1       jfb       110:        size_t len;
                    111:        char path[MAXPATHLEN], buf[MAXNAMLEN];
                    112:        FILE *ifp;
                    113:
                    114:        TAILQ_INIT(&cvs_ign_pats);
                    115:
1.53      jfb       116:        if ((cvs_addedrev = rcsnum_parse("0")) == NULL)
1.46      jfb       117:                return (-1);
1.4       jfb       118:
1.1       jfb       119:        /* standard patterns to ignore */
1.10      jfb       120:        for (i = 0; i < (int)(sizeof(cvs_ign_std)/sizeof(char *)); i++)
1.26      jfb       121:                cvs_file_ignore(cvs_ign_std[i]);
1.1       jfb       122:
                    123:        /* read the cvsignore file in the user's home directory, if any */
1.111     xsa       124:        l = snprintf(path, sizeof(path), "%s/.cvsignore", cvs_homedir);
                    125:        if (l == -1 || l >= (int)sizeof(path)) {
                    126:                errno = ENAMETOOLONG;
                    127:                cvs_log(LP_ERRNO, "%s", path);
                    128:                return (-1);
                    129:        }
1.61      xsa       130:
1.111     xsa       131:        ifp = fopen(path, "r");
                    132:        if (ifp == NULL) {
                    133:                if (errno != ENOENT)
                    134:                        cvs_log(LP_ERRNO,
                    135:                            "failed to open user's cvsignore file `%s'", path);
                    136:        } else {
1.112     xsa       137:                while (fgets(buf, (int)sizeof(buf), ifp) != NULL) {
1.111     xsa       138:                        len = strlen(buf);
                    139:                        if (len == 0)
                    140:                                continue;
                    141:                        if (buf[len - 1] != '\n') {
                    142:                                cvs_log(LP_ERR, "line too long in `%s'", path);
1.1       jfb       143:                        }
1.111     xsa       144:                        buf[--len] = '\0';
                    145:                        cvs_file_ignore(buf);
1.1       jfb       146:                }
1.111     xsa       147:                (void)fclose(ifp);
1.1       jfb       148:        }
                    149:
                    150:        return (0);
                    151: }
                    152:
                    153: /*
                    154:  * cvs_file_ignore()
                    155:  *
                    156:  * Add the pattern <pat> to the list of patterns for files to ignore.
                    157:  * Returns 0 on success, or -1 on failure.
                    158:  */
                    159: int
                    160: cvs_file_ignore(const char *pat)
                    161: {
                    162:        char *cp;
                    163:        struct cvs_ignpat *ip;
                    164:
1.136     ray       165:        ip = xmalloc(sizeof(*ip));
1.1       jfb       166:        strlcpy(ip->ip_pat, pat, sizeof(ip->ip_pat));
                    167:
                    168:        /* check if we will need globbing for that pattern */
                    169:        ip->ip_flags = CVS_IGN_STATIC;
                    170:        for (cp = ip->ip_pat; *cp != '\0'; cp++) {
                    171:                if (CVS_CHAR_ISMETA(*cp)) {
                    172:                        ip->ip_flags &= ~CVS_IGN_STATIC;
                    173:                        break;
                    174:                }
                    175:        }
                    176:
                    177:        TAILQ_INSERT_TAIL(&cvs_ign_pats, ip, ip_list);
                    178:
                    179:        return (0);
                    180: }
                    181:
                    182: /*
1.5       jfb       183:  * cvs_file_chkign()
1.1       jfb       184:  *
                    185:  * Returns 1 if the filename <file> is matched by one of the ignore
                    186:  * patterns, or 0 otherwise.
                    187:  */
                    188: int
1.5       jfb       189: cvs_file_chkign(const char *file)
1.1       jfb       190: {
1.23      jfb       191:        int flags;
1.1       jfb       192:        struct cvs_ignpat *ip;
                    193:
1.23      jfb       194:        flags = FNM_PERIOD;
                    195:        if (cvs_nocase)
                    196:                flags |= FNM_CASEFOLD;
                    197:
1.1       jfb       198:        TAILQ_FOREACH(ip, &cvs_ign_pats, ip_list) {
                    199:                if (ip->ip_flags & CVS_IGN_STATIC) {
1.23      jfb       200:                        if (cvs_file_cmpname(file, ip->ip_pat) == 0)
1.1       jfb       201:                                return (1);
1.38      deraadt   202:                } else if (fnmatch(ip->ip_pat, file, flags) == 0)
1.1       jfb       203:                        return (1);
                    204:        }
                    205:
                    206:        return (0);
                    207: }
                    208:
                    209: /*
1.6       jfb       210:  * cvs_file_create()
1.1       jfb       211:  *
1.6       jfb       212:  * Create a new file whose path is specified in <path> and of type <type>.
1.26      jfb       213:  * If the type is DT_DIR, the CVS administrative repository and files will be
                    214:  * created.
1.25      jfb       215:  * Returns the created file on success, or NULL on failure.
1.1       jfb       216:  */
1.109     xsa       217: CVSFILE *
1.34      jfb       218: cvs_file_create(CVSFILE *parent, const char *path, u_int type, mode_t mode)
1.1       jfb       219: {
1.85      joris     220:        int fd, l;
                    221:        char fp[MAXPATHLEN], repo[MAXPATHLEN];
1.6       jfb       222:        CVSFILE *cfp;
1.1       jfb       223:
1.6       jfb       224:        cfp = cvs_file_alloc(path, type);
                    225:        if (cfp == NULL)
                    226:                return (NULL);
1.26      jfb       227:
1.100     joris     228:        l = 0;
1.22      jfb       229:        cfp->cf_mode = mode;
1.34      jfb       230:        cfp->cf_parent = parent;
1.1       jfb       231:
1.34      jfb       232:        if (type == DT_DIR) {
1.62      jfb       233:                cfp->cf_root = cvsroot_get(path);
1.84      joris     234:                if (cfp->cf_root == NULL) {
1.100     joris     235:                        cvs_file_free(cfp);
1.84      joris     236:                        return (NULL);
                    237:                }
                    238:
1.85      joris     239:                if (cvs_repo_base != NULL) {
                    240:                        cvs_file_getpath(cfp, fp, sizeof(fp));
                    241:                        l = snprintf(repo, sizeof(repo), "%s/%s", cvs_repo_base,
                    242:                            fp);
                    243:                } else {
                    244:                        cvs_file_getpath(cfp, repo, sizeof(repo));
                    245:                        l = 0;
                    246:                }
                    247:
                    248:                if (l == -1 || l >= (int)sizeof(repo)) {
                    249:                        errno = ENAMETOOLONG;
                    250:                        cvs_log(LP_ERRNO, "%s", repo);
                    251:                        cvs_file_free(cfp);
                    252:                        return (NULL);
                    253:                }
                    254:
1.132     joris     255:                cfp->cf_repo = xstrdup(repo);
1.137   ! deraadt   256:                if ((mkdir(path, mode) == -1 && errno != EEXIST) ||
        !           257:                    cvs_mkadmin(path, cfp->cf_root->cr_str, cfp->cf_repo,
        !           258:                    NULL, NULL, 0) < 0) {
1.6       jfb       259:                        cvs_file_free(cfp);
                    260:                        return (NULL);
1.50      jfb       261:                }
1.38      deraadt   262:        } else {
1.6       jfb       263:                fd = open(path, O_WRONLY|O_CREAT|O_EXCL, mode);
                    264:                if (fd == -1) {
                    265:                        cvs_file_free(cfp);
1.1       jfb       266:                        return (NULL);
                    267:                }
1.6       jfb       268:                (void)close(fd);
1.1       jfb       269:        }
                    270:
1.6       jfb       271:        return (cfp);
1.3       jfb       272: }
                    273:
                    274:
                    275: /*
1.35      jfb       276:  * cvs_file_copy()
                    277:  *
                    278:  * Allocate space to create a copy of the file <orig>.  The copy inherits all
                    279:  * of the original's attributes, but does not inherit its children if the
                    280:  * original file is a directory.  Note that files copied using this mechanism
                    281:  * are linked to their parent, but the parent has no link to the file.  This
                    282:  * is so cvs_file_getpath() works.
                    283:  * Returns the copied file on success, or NULL on failure.  The returned
                    284:  * structure should be freed using cvs_file_free().
                    285:  */
1.109     xsa       286: CVSFILE *
1.35      jfb       287: cvs_file_copy(CVSFILE *orig)
                    288: {
                    289:        char path[MAXPATHLEN];
                    290:        CVSFILE *cfp;
                    291:
                    292:        cvs_file_getpath(orig, path, sizeof(path));
                    293:
                    294:        cfp = cvs_file_alloc(path, orig->cf_type);
                    295:        if (cfp == NULL)
                    296:                return (NULL);
                    297:
                    298:        cfp->cf_parent = orig->cf_parent;
                    299:        cfp->cf_mode = orig->cf_mode;
                    300:        cfp->cf_cvstat = orig->cf_cvstat;
                    301:
1.94      joris     302:        if (orig->cf_type == DT_REG) {
                    303:                cfp->cf_etime = orig->cf_etime;
1.62      jfb       304:                cfp->cf_mtime = orig->cf_mtime;
1.94      joris     305:        } else if (orig->cf_type == DT_DIR) {
1.35      jfb       306:                /* XXX copy CVS directory attributes */
                    307:        }
                    308:
                    309:        return (cfp);
                    310: }
                    311:
                    312:
                    313: /*
1.3       jfb       314:  * cvs_file_get()
                    315:  *
                    316:  * Load a cvs_file structure with all the information pertaining to the file
                    317:  * <path>.
1.4       jfb       318:  * The <flags> parameter specifies various flags that alter the behaviour of
1.21      jfb       319:  * the function.  The CF_RECURSE flag causes the function to recursively load
                    320:  * subdirectories when <path> is a directory.
                    321:  * The CF_SORT flag causes the files to be sorted in alphabetical order upon
                    322:  * loading.  The special case of "." as a path specification generates
                    323:  * recursion for a single level and is equivalent to calling cvs_file_get() on
                    324:  * all files of that directory.
1.3       jfb       325:  * Returns a pointer to the cvs file structure, which must later be freed
                    326:  * with cvs_file_free().
                    327:  */
                    328:
1.100     joris     329: int
1.73      joris     330: cvs_file_get(const char *path, int flags, int (*cb)(CVSFILE *, void *),
1.100     joris     331:     void *arg, struct cvs_flist *list)
1.3       jfb       332: {
1.68      joris     333:        char *files[1];
                    334:
1.105     joris     335:        *(const char **)files = path;
1.100     joris     336:        return cvs_file_getspec(files, 1, flags, cb, arg, list);
1.9       jfb       337: }
                    338:
                    339:
                    340: /*
                    341:  * cvs_file_getspec()
                    342:  *
1.100     joris     343:  * Obtain the info about the supplied files or directories.
1.9       jfb       344:  */
1.100     joris     345: int
1.73      joris     346: cvs_file_getspec(char **fspec, int fsn, int flags, int (*cb)(CVSFILE *, void *),
1.100     joris     347:     void *arg, struct cvs_flist *list)
1.9       jfb       348: {
1.100     joris     349:        int i, freecf;
                    350:        char pcopy[MAXPATHLEN];
                    351:        CVSFILE *cf;
                    352:        extern char *cvs_rootstr;
                    353:
                    354:        freecf = (list == NULL);
                    355:        cvs_error = CVS_EX_DATA;
                    356:
                    357:        /* init the list */
                    358:        if (list != NULL)
                    359:                SIMPLEQ_INIT(list);
1.26      jfb       360:
1.100     joris     361:        /*
                    362:         * Fetch the needed information about ".", so we can setup a few
                    363:         * things to get ourselfs going.
                    364:         */
1.115     joris     365:        cf = cvs_file_lget(".", 0, NULL, NULL, NULL);
1.100     joris     366:        if (cf == NULL) {
1.125     joris     367:                cvs_log(LP_ERR, "failed to obtain '.' information");
1.100     joris     368:                return (-1);
                    369:        }
1.87      joris     370:
1.85      joris     371:        /*
1.100     joris     372:         * save the base repository path so we can use it to create
                    373:         * the correct full repopath later on.
1.85      joris     374:         */
1.100     joris     375:        if (cf->cf_repo != NULL) {
                    376:                if (cvs_repo_base != NULL)
1.132     joris     377:                        xfree(cvs_repo_base);
                    378:                cvs_repo_base = xstrdup(cf->cf_repo);
1.85      joris     379:        }
1.26      jfb       380:
1.95      joris     381:        /*
1.100     joris     382:         * This will go away when we have support for multiple Roots.
1.95      joris     383:         */
1.100     joris     384:        if (cvs_rootstr == NULL && cf->cf_root != NULL) {
1.132     joris     385:                cvs_rootstr = xstrdup(cf->cf_root->cr_str);
1.73      joris     386:        }
                    387:
1.100     joris     388:        cvs_error = CVS_EX_OK;
                    389:
                    390:        /*
                    391:         * Since some commands don't require any files to operate
                    392:         * we can stop right here for those.
                    393:         */
1.128     joris     394:        if (cf->cf_root != NULL) {
                    395:                if (cf->cf_root->cr_method != CVS_METHOD_LOCAL &&
                    396:                    cvs_cmdop == CVS_OP_CHECKOUT) {
                    397:                        cvs_file_free(cf);
                    398:                        return (0);
                    399:                }
                    400:        }
                    401:
                    402:        cvs_file_free(cf);
                    403:
                    404:        if (cvs_cmdop == CVS_OP_VERSION)
1.100     joris     405:                return (0);
                    406:
1.26      jfb       407:        for (i = 0; i < fsn; i++) {
                    408:                strlcpy(pcopy, fspec[i], sizeof(pcopy));
1.123     joris     409:
                    410:                /*
                    411:                 * get rid of any trailing slashes.
                    412:                 */
                    413:                STRIP_SLASH(pcopy);
1.26      jfb       414:
1.100     joris     415:                /*
                    416:                 * Load the information.
                    417:                 */
                    418:                cf = cvs_file_loadinfo(pcopy, flags, cb, arg, freecf);
1.108     joris     419:                if (cf == NULL) {
                    420:                        if (cvs_error != CVS_EX_OK)
1.126     joris     421:                                return (-1);
1.100     joris     422:                        continue;
1.108     joris     423:                }
1.68      joris     424:
1.100     joris     425:                /*
                    426:                 * If extra actions are needed, do them now.
                    427:                 */
                    428:                if (cf->cf_type == DT_DIR) {
                    429:                        /* do possible extra actions .. */
1.68      joris     430:                } else {
1.100     joris     431:                        /* do possible extra actions .. */
                    432:                }
                    433:
                    434:                /*
                    435:                 * Attach it to a list if requested, otherwise
                    436:                 * just free it again.
                    437:                 */
                    438:                if (list != NULL)
                    439:                        SIMPLEQ_INSERT_TAIL(list, cf, cf_list);
                    440:                else
                    441:                        cvs_file_free(cf);
                    442:        }
                    443:
                    444:        return (0);
                    445: }
                    446:
                    447: /*
                    448:  * Load the neccesary information about file or directory <path>.
                    449:  * Returns a pointer to the loaded information on success, or NULL
                    450:  * on failure.
                    451:  *
                    452:  * If cb is not NULL, the requested path will be passed to that callback
                    453:  * with <arg> as an argument.
                    454:  *
                    455:  * the <freecf> argument is passed to cvs_file_getdir, if this is 1
                    456:  * CVSFILE * structs will be free'd once we are done with them.
                    457:  */
1.114     xsa       458: CVSFILE *
1.100     joris     459: cvs_file_loadinfo(char *path, int flags, int (*cb)(CVSFILE *, void *),
                    460:     void *arg, int freecf)
                    461: {
                    462:        CVSFILE *cf, *base;
                    463:        CVSENTRIES *entf;
                    464:        struct cvs_ent *ent;
                    465:        char *p;
                    466:        char parent[MAXPATHLEN], item[MAXPATHLEN];
1.120     joris     467:        int type, callit;
1.100     joris     468:        struct stat st;
                    469:        struct cvsroot *root;
                    470:
                    471:        type = 0;
                    472:        base = cf = NULL;
                    473:        entf = NULL;
                    474:        ent = NULL;
                    475:
                    476:        /*
                    477:         * We first have to find out what type of item we are
                    478:         * dealing with. A file or a directory.
                    479:         *
                    480:         * We can do this by stat(2)'ing the item, but since it
                    481:         * might be gone we also check the Entries file in the
                    482:         * parent directory.
                    483:         */
                    484:
                    485:        /* get parent directory */
                    486:        if ((p = strrchr(path, '/')) != NULL) {
                    487:                *p++ = '\0';
                    488:                strlcpy(parent, path, sizeof(parent));
                    489:                strlcpy(item, p, sizeof(item));
                    490:                *--p = '/';
                    491:        } else {
                    492:                strlcpy(parent, ".", sizeof(parent));
                    493:                strlcpy(item, path, sizeof(item));
                    494:        }
                    495:
                    496:        /*
                    497:         * There might not be an Entries file, so do not fail if there
                    498:         * is none available to get the info from.
                    499:         */
1.115     joris     500:        entf = cvs_ent_open(parent, O_RDWR);
1.100     joris     501:
                    502:        /*
                    503:         * Load the Entry if we successfully opened the Entries file.
                    504:         */
                    505:        if (entf != NULL)
                    506:                ent = cvs_ent_get(entf, item);
                    507:
                    508:        /*
                    509:         * No Entry available? fall back to stat(2)'ing the item, if
1.114     xsa       510:         * that fails, assume a normal file.
1.100     joris     511:         */
                    512:        if (ent == NULL) {
1.103     joris     513:                if (stat(path, &st) == -1)
1.100     joris     514:                        type = DT_REG;
1.103     joris     515:                else
1.100     joris     516:                        type = IFTODT(st.st_mode);
                    517:        } else {
                    518:                if (ent->ce_type == CVS_ENT_DIR)
                    519:                        type = DT_DIR;
                    520:                else
                    521:                        type = DT_REG;
                    522:        }
                    523:
                    524:        /*
                    525:         * Get the base, which is <parent> for a normal file or
                    526:         * <path> for a directory.
                    527:         */
                    528:        if (type == DT_DIR)
1.115     joris     529:                base = cvs_file_lget(path, flags, NULL, entf, ent);
1.100     joris     530:        else
1.115     joris     531:                base = cvs_file_lget(parent, flags, NULL, entf, NULL);
1.100     joris     532:
                    533:        if (base == NULL) {
                    534:                cvs_log(LP_ERR, "failed to obtain directory info for '%s'",
                    535:                    parent);
1.108     joris     536:                cvs_error = CVS_EX_FILE;
1.100     joris     537:                goto fail;
                    538:        }
                    539:
                    540:        /*
                    541:         * Sanity.
                    542:         */
                    543:        if (base->cf_type != DT_DIR) {
                    544:                cvs_log(LP_ERR, "base directory isn't a directory at all");
                    545:                goto fail;
                    546:        }
                    547:
                    548:        root = CVS_DIR_ROOT(base);
                    549:        if (root == NULL) {
1.108     joris     550:                cvs_error = CVS_EX_BADROOT;
1.100     joris     551:                goto fail;
                    552:        }
1.26      jfb       553:
1.100     joris     554:        /*
                    555:         * If we have a normal file, get the info and link it
                    556:         * to the base.
                    557:         */
                    558:        if (type != DT_DIR) {
1.115     joris     559:                cf = cvs_file_lget(path, flags, base, entf, ent);
1.100     joris     560:                if (cf == NULL) {
1.108     joris     561:                        cvs_error = CVS_EX_DATA;
1.100     joris     562:                        goto fail;
1.68      joris     563:                }
1.26      jfb       564:
1.100     joris     565:                cvs_file_attach(base, cf);
                    566:        }
                    567:
                    568:        /*
                    569:         * Always pass the base directory, unless:
                    570:         * - we are running in server or local mode and the path is not "."
                    571:         * - the directory does not exist on disk.
                    572:         * - the callback is NULL.
1.122     xsa       573:        */
1.120     joris     574:        callit = 1;
                    575:        if (cb == NULL)
                    576:                callit = 0;
                    577:
1.137   ! deraadt   578:        if (cvs_cmdop == CVS_OP_SERVER && type != DT_DIR)
1.120     joris     579:                callit = 0;
                    580:
1.137   ! deraadt   581:        if (root->cr_method == CVS_METHOD_LOCAL && type != DT_DIR)
1.120     joris     582:                callit = 0;
                    583:
                    584:        if (!(base->cf_flags & CVS_FILE_ONDISK))
                    585:                callit = 0;
                    586:
                    587:        if (callit != 0) {
                    588:                if ((cvs_error = cb(base,arg)) != CVS_EX_OK)
                    589:                        goto fail;
                    590:        }
1.100     joris     591:
                    592:        /*
                    593:         * If we have a normal file, pass it as well.
                    594:         */
                    595:        if (type != DT_DIR) {
1.137   ! deraadt   596:                if (cb != NULL && (cvs_error = cb(cf, arg)) != CVS_EX_OK)
1.100     joris     597:                        goto fail;
                    598:        } else {
                    599:                /*
                    600:                 * If the directory exists, recurse through it.
                    601:                 */
                    602:                if ((base->cf_flags & CVS_FILE_ONDISK) &&
                    603:                    cvs_file_getdir(base, flags, cb, arg, freecf) < 0) {
1.108     joris     604:                        cvs_error = CVS_EX_FILE;
1.100     joris     605:                        goto fail;
1.68      joris     606:                }
1.116     joris     607:        }
                    608:
                    609:        if (entf != NULL) {
                    610:                cvs_ent_close(entf);
                    611:                entf = NULL;
1.26      jfb       612:        }
1.87      joris     613:
1.100     joris     614:        return (base);
1.26      jfb       615:
1.100     joris     616: fail:
                    617:        if (entf != NULL)
                    618:                cvs_ent_close(entf);
                    619:        if (base != NULL)
                    620:                cvs_file_free(base);
                    621:        return (NULL);
1.3       jfb       622: }
                    623:
                    624: /*
1.13      jfb       625:  * cvs_file_find()
                    626:  *
                    627:  * Find the pointer to a CVS file entry within the file hierarchy <hier>.
                    628:  * The file's pathname <path> must be relative to the base of <hier>.
                    629:  * Returns the entry on success, or NULL on failure.
                    630:  */
1.109     xsa       631: CVSFILE *
1.13      jfb       632: cvs_file_find(CVSFILE *hier, const char *path)
                    633: {
                    634:        char *pp, *sp, pbuf[MAXPATHLEN];
                    635:        CVSFILE *sf, *cf;
                    636:
                    637:        strlcpy(pbuf, path, sizeof(pbuf));
                    638:
                    639:        cf = hier;
                    640:        pp = pbuf;
                    641:        do {
                    642:                sp = strchr(pp, '/');
                    643:                if (sp != NULL)
1.24      jfb       644:                        *(sp++) = '\0';
1.13      jfb       645:
                    646:                /* special case */
                    647:                if (*pp == '.') {
1.137   ! deraadt   648:                        if (*(pp + 1) == '.' && *(pp + 2) == '\0') {
1.13      jfb       649:                                /* request to go back to parent */
                    650:                                if (cf->cf_parent == NULL) {
                    651:                                        cvs_log(LP_NOTICE,
                    652:                                            "path %s goes back too far", path);
                    653:                                        return (NULL);
                    654:                                }
                    655:                                cf = cf->cf_parent;
                    656:                                continue;
1.38      deraadt   657:                        } else if (*(pp + 1) == '\0')
1.13      jfb       658:                                continue;
                    659:                }
                    660:
1.62      jfb       661:                SIMPLEQ_FOREACH(sf, &(cf->cf_files), cf_list)
1.99      joris     662:                        if (cvs_file_cmpname(pp, sf->cf_name) == 0)
1.13      jfb       663:                                break;
                    664:                if (sf == NULL)
                    665:                        return (NULL);
                    666:
                    667:                cf = sf;
                    668:                pp = sp;
                    669:        } while (sp != NULL);
                    670:
1.24      jfb       671:        return (cf);
1.13      jfb       672: }
                    673:
                    674:
                    675: /*
1.34      jfb       676:  * cvs_file_getpath()
                    677:  *
                    678:  * Get the full path of the file <file> and store it in <buf>, which is of
                    679:  * size <len>.  For portability, it is recommended that <buf> always be
                    680:  * at least MAXPATHLEN bytes long.
1.100     joris     681:  * Returns a pointer to the start of the path.
1.34      jfb       682:  */
1.104     xsa       683: char *
1.34      jfb       684: cvs_file_getpath(CVSFILE *file, char *buf, size_t len)
                    685: {
1.100     joris     686:        memset(buf, '\0', len);
                    687:        if (file->cf_dir != NULL) {
                    688:                strlcat(buf, file->cf_dir, len);
1.34      jfb       689:                strlcat(buf, "/", len);
                    690:        }
                    691:
1.100     joris     692:        strlcat(buf, file->cf_name, len);
1.34      jfb       693:        return (buf);
                    694: }
                    695:
                    696: /*
1.22      jfb       697:  * cvs_file_attach()
                    698:  *
                    699:  * Attach the file <file> as one of the children of parent <parent>, which
                    700:  * has to be a file of type DT_DIR.
                    701:  * Returns 0 on success, or -1 on failure.
                    702:  */
                    703: int
                    704: cvs_file_attach(CVSFILE *parent, CVSFILE *file)
                    705: {
                    706:        if (parent->cf_type != DT_DIR)
                    707:                return (-1);
                    708:
1.62      jfb       709:        SIMPLEQ_INSERT_TAIL(&(parent->cf_files), file, cf_list);
1.22      jfb       710:        file->cf_parent = parent;
                    711:
                    712:        return (0);
                    713: }
                    714:
                    715:
                    716: /*
1.68      joris     717:  * Load directory information
1.3       jfb       718:  */
1.6       jfb       719: static int
1.69      joris     720: cvs_load_dirinfo(CVSFILE *cf, int flags)
1.3       jfb       721: {
1.68      joris     722:        char fpath[MAXPATHLEN];
                    723:        char pbuf[MAXPATHLEN];
1.20      jfb       724:        struct stat st;
1.68      joris     725:        int l;
1.7       jfb       726:
1.34      jfb       727:        cvs_file_getpath(cf, fpath, sizeof(fpath));
1.100     joris     728:
                    729:        /*
                    730:         * Try to obtain the Root for this given directory, if we cannot
                    731:         * get it, fail, unless we are dealing with a directory that is
                    732:         * unknown or not on disk.
                    733:         */
1.62      jfb       734:        cf->cf_root = cvsroot_get(fpath);
1.91      joris     735:        if (cf->cf_root == NULL) {
1.100     joris     736:                if (cf->cf_cvstat == CVS_FST_UNKNOWN ||
                    737:                    !(cf->cf_flags & CVS_FILE_ONDISK))
1.91      joris     738:                        return (0);
1.48      jfb       739:                return (-1);
1.91      joris     740:        }
1.114     xsa       741:
1.74      joris     742:        /* if the CVS administrative directory exists, load the info */
                    743:        l = snprintf(pbuf, sizeof(pbuf), "%s/" CVS_PATH_CVSDIR, fpath);
                    744:        if (l == -1 || l >= (int)sizeof(pbuf)) {
                    745:                errno = ENAMETOOLONG;
                    746:                cvs_log(LP_ERRNO, "%s", pbuf);
                    747:                return (-1);
                    748:        }
1.61      xsa       749:
1.137   ! deraadt   750:        if (stat(pbuf, &st) == 0 && S_ISDIR(st.st_mode)) {
1.132     joris     751:                if (cvs_readrepo(fpath, pbuf, sizeof(pbuf)) == 0)
                    752:                        cf->cf_repo = xstrdup(pbuf);
1.100     joris     753:        } else {
                    754:                /*
                    755:                 * Fill in the repo path ourselfs.
                    756:                 */
1.107     joris     757:                if (cvs_repo_base != NULL) {
                    758:                        l = snprintf(pbuf, sizeof(pbuf), "%s/%s",
                    759:                            cvs_repo_base, fpath);
                    760:                        if (l == -1 || l >= (int)sizeof(pbuf))
                    761:                                return (-1);
1.100     joris     762:
1.132     joris     763:                        cf->cf_repo = xstrdup(pbuf);
1.107     joris     764:                } else
                    765:                        cf->cf_repo = NULL;
1.68      joris     766:        }
1.100     joris     767:
1.68      joris     768:        return (0);
                    769: }
                    770:
                    771: /*
                    772:  * cvs_file_getdir()
                    773:  *
                    774:  * Get a cvs directory structure for the directory whose path is <dir>.
                    775:  * This function should not free the directory information on error, as this
                    776:  * is performed by cvs_file_free().
                    777:  */
                    778: static int
1.100     joris     779: cvs_file_getdir(CVSFILE *cf, int flags, int (*cb)(CVSFILE *, void *),
                    780:     void *arg, int freecf)
1.68      joris     781: {
1.100     joris     782:        int ret;
                    783:        size_t len;
                    784:        DIR *dp;
                    785:        struct dirent *de;
                    786:        char fpath[MAXPATHLEN], pbuf[MAXPATHLEN];
                    787:        CVSENTRIES *entf;
1.76      joris     788:        CVSFILE *cfp;
1.100     joris     789:        struct cvs_ent *ent;
1.68      joris     790:        struct cvs_flist dirs;
1.100     joris     791:        int nfiles, ndirs;
1.14      jfb       792:
1.137   ! deraadt   793:        if ((flags & CF_KNOWN) && cf->cf_cvstat == CVS_FST_UNKNOWN)
1.26      jfb       794:                return (0);
                    795:
1.128     joris     796:        /*
                    797:         * if we are working with a repository, fiddle with
                    798:         * the pathname again.
                    799:         */
                    800:        if (flags & CF_REPO) {
                    801:                ret = snprintf(fpath, sizeof(fpath), "%s%s%s",
                    802:                    cf->cf_root->cr_dir,
                    803:                    (cf->cf_dir != NULL) ? "/" : "",
                    804:                    (cf->cf_dir != NULL) ? cf->cf_dir : "");
                    805:                if (ret == -1 || ret >= (int)sizeof(fpath))
                    806:                        return (-1);
1.133     joris     807:
                    808:                if (cf->cf_dir != NULL)
                    809:                        xfree(cf->cf_dir);
1.132     joris     810:                cf->cf_dir = xstrdup(fpath);
1.128     joris     811:        }
                    812:
1.100     joris     813:        nfiles = ndirs = 0;
                    814:        SIMPLEQ_INIT(&dirs);
                    815:        cvs_file_getpath(cf, fpath, sizeof(fpath));
1.73      joris     816:
1.100     joris     817:        if ((dp = opendir(fpath)) == NULL) {
                    818:                cvs_log(LP_ERRNO, "failed to open directory '%s'", fpath);
1.91      joris     819:                return (-1);
                    820:        }
                    821:
1.100     joris     822:        ret = -1;
1.115     joris     823:        entf = cvs_ent_open(fpath, O_RDWR);
1.100     joris     824:        while ((de = readdir(dp)) != NULL) {
                    825:                if (!strcmp(de->d_name, ".") ||
                    826:                    !strcmp(de->d_name, ".."))
1.68      joris     827:                        continue;
1.11      jfb       828:
1.117     joris     829:                len = cvs_path_cat(fpath, de->d_name, pbuf, sizeof(pbuf));
                    830:                if (len >= sizeof(pbuf))
                    831:                        goto done;
                    832:
                    833:                if (entf != NULL)
                    834:                        ent = cvs_ent_get(entf, de->d_name);
                    835:                else
                    836:                        ent = NULL;
                    837:
1.100     joris     838:                /*
                    839:                 * Do some filtering on the current directory item.
                    840:                 */
                    841:                if ((flags & CF_IGNORE) && cvs_file_chkign(de->d_name))
1.68      joris     842:                        continue;
1.24      jfb       843:
1.100     joris     844:                if (!(flags & CF_RECURSE) && (de->d_type == DT_DIR)) {
1.117     joris     845:                        if (ent != NULL)
                    846:                                ent->processed = 1;
1.68      joris     847:                        continue;
                    848:                }
1.70      joris     849:
1.137   ! deraadt   850:                if (de->d_type != DT_DIR && (flags & CF_NOFILES))
1.70      joris     851:                        continue;
1.56      jfb       852:
1.115     joris     853:                cfp = cvs_file_lget(pbuf, flags, cf, entf, ent);
1.100     joris     854:                if (cfp == NULL) {
                    855:                        cvs_log(LP_ERR, "failed to get '%s'", pbuf);
1.91      joris     856:                        goto done;
1.68      joris     857:                }
1.61      xsa       858:
1.100     joris     859:                /*
                    860:                 * A file is linked to the parent <cf>, a directory
                    861:                 * is added to the dirs SIMPLEQ list for later use.
                    862:                 */
1.137   ! deraadt   863:                if (cfp->cf_type != DT_DIR && !freecf) {
1.100     joris     864:                        SIMPLEQ_INSERT_TAIL(&(cf->cf_files), cfp, cf_list);
                    865:                        nfiles++;
                    866:                } else if (cfp->cf_type == DT_DIR) {
                    867:                        SIMPLEQ_INSERT_TAIL(&dirs, cfp, cf_list);
                    868:                        ndirs++;
                    869:                }
1.71      joris     870:
1.100     joris     871:                /*
                    872:                 * Now, for a file, pass it to the callback if it was
                    873:                 * supplied to us.
                    874:                 */
                    875:                if (cfp->cf_type != DT_DIR && cb != NULL) {
                    876:                        if ((cvs_error = cb(cfp, arg)) != CVS_EX_OK)
1.91      joris     877:                                goto done;
1.4       jfb       878:                }
1.14      jfb       879:
1.100     joris     880:                /*
1.117     joris     881:                 * Mark the entry as processed.
1.100     joris     882:                 */
1.117     joris     883:                if (ent != NULL)
                    884:                        ent->processed = 1;
1.68      joris     885:
1.100     joris     886:                /*
                    887:                 * If we don't want to keep it, free it
                    888:                 */
1.137   ! deraadt   889:                if (cfp->cf_type != DT_DIR && freecf)
1.100     joris     890:                        cvs_file_free(cfp);
1.68      joris     891:        }
                    892:
1.100     joris     893:        closedir(dp);
                    894:        dp = NULL;
1.68      joris     895:
1.100     joris     896:        /*
                    897:         * Pass over all of the entries now, so we pickup any files
                    898:         * that might have been lost, or are for some reason not on disk.
                    899:         *
                    900:         * (Follows the same procedure as above ... can we merge them?)
                    901:         */
1.137   ! deraadt   902:        while (entf != NULL && (ent = cvs_ent_next(entf)) != NULL) {
1.117     joris     903:                if (ent->processed == 1)
                    904:                        continue;
1.137   ! deraadt   905:                if (!(flags & CF_RECURSE) && ent->ce_type == CVS_ENT_DIR)
1.100     joris     906:                        continue;
1.137   ! deraadt   907:                if ((flags & CF_NOFILES) && ent->ce_type != CVS_ENT_DIR)
1.100     joris     908:                        continue;
1.68      joris     909:
1.100     joris     910:                len = cvs_path_cat(fpath, ent->ce_name, pbuf, sizeof(pbuf));
                    911:                if (len >= sizeof(pbuf))
                    912:                        goto done;
1.61      xsa       913:
1.115     joris     914:                cfp = cvs_file_lget(pbuf, flags, cf, entf, ent);
1.100     joris     915:                if (cfp == NULL) {
                    916:                        cvs_log(LP_ERR, "failed to fetch '%s'", pbuf);
                    917:                        goto done;
                    918:                }
1.68      joris     919:
1.137   ! deraadt   920:                if (cfp->cf_type != DT_DIR && !freecf) {
1.100     joris     921:                        SIMPLEQ_INSERT_TAIL(&(cf->cf_files), cfp, cf_list);
                    922:                        nfiles++;
                    923:                } else if (cfp->cf_type == DT_DIR) {
                    924:                        SIMPLEQ_INSERT_TAIL(&dirs, cfp, cf_list);
                    925:                        ndirs++;
                    926:                }
1.68      joris     927:
1.100     joris     928:                if (cfp->cf_type != DT_DIR && cb != NULL) {
                    929:                        if ((cvs_error = cb(cfp, arg)) != CVS_EX_OK)
                    930:                                goto done;
                    931:                }
1.68      joris     932:
1.137   ! deraadt   933:                if (cfp->cf_type != DT_DIR && freecf)
1.100     joris     934:                        cvs_file_free(cfp);
1.40      jfb       935:        }
1.34      jfb       936:
1.100     joris     937:        /*
                    938:         * Sort files and dirs if requested.
                    939:         */
1.10      jfb       940:        if (flags & CF_SORT) {
1.68      joris     941:                if (nfiles > 0)
                    942:                        cvs_file_sort(&(cf->cf_files), nfiles);
                    943:                if (ndirs > 0)
                    944:                        cvs_file_sort(&dirs, ndirs);
1.10      jfb       945:        }
1.26      jfb       946:
1.100     joris     947:        /*
                    948:         * Finally, run over the directories we have encountered.
                    949:         * Before calling cvs_file_getdir() on them, we pass them
                    950:         * to the callback first.
                    951:         */
1.62      jfb       952:        while (!SIMPLEQ_EMPTY(&dirs)) {
                    953:                cfp = SIMPLEQ_FIRST(&dirs);
                    954:                SIMPLEQ_REMOVE_HEAD(&dirs, cf_list);
1.68      joris     955:
1.100     joris     956:                if (!freecf)
1.68      joris     957:                        SIMPLEQ_INSERT_TAIL(&(cf->cf_files), cfp, cf_list);
                    958:
1.100     joris     959:                if (cb != NULL) {
                    960:                        if ((cvs_error = cb(cfp, arg)) != CVS_EX_OK)
                    961:                                goto done;
1.68      joris     962:                }
1.100     joris     963:
                    964:                if ((cfp->cf_flags & CVS_FILE_ONDISK) &&
1.137   ! deraadt   965:                    cvs_file_getdir(cfp, flags, cb, arg, freecf) < 0)
1.100     joris     966:                        goto done;
                    967:
                    968:                if (freecf)
                    969:                        cvs_file_free(cfp);
1.26      jfb       970:        }
1.3       jfb       971:
1.91      joris     972:        ret = 0;
1.100     joris     973:        cfp = NULL;
1.91      joris     974: done:
1.137   ! deraadt   975:        if (cfp != NULL && freecf)
1.100     joris     976:                cvs_file_free(cfp);
                    977:
                    978:        while (!SIMPLEQ_EMPTY(&dirs)) {
                    979:                cfp = SIMPLEQ_FIRST(&dirs);
                    980:                SIMPLEQ_REMOVE_HEAD(&dirs, cf_list);
                    981:
                    982:                cvs_file_free(cfp);
                    983:        }
                    984:
                    985:        if (entf != NULL)
                    986:                cvs_ent_close(entf);
                    987:        if (dp != NULL)
                    988:                closedir(dp);
1.91      joris     989:
                    990:        return (ret);
1.3       jfb       991: }
                    992:
                    993:
                    994: /*
                    995:  * cvs_file_free()
                    996:  *
                    997:  * Free a cvs_file structure and its contents.
                    998:  */
                    999: void
1.14      jfb      1000: cvs_file_free(CVSFILE *cf)
1.3       jfb      1001: {
1.62      jfb      1002:        CVSFILE *child;
                   1003:
1.47      jfb      1004:        if (cf->cf_name != NULL)
1.132     joris    1005:                xfree(cf->cf_name);
1.62      jfb      1006:
1.100     joris    1007:        if (cf->cf_dir != NULL)
1.132     joris    1008:                xfree(cf->cf_dir);
1.100     joris    1009:
1.62      jfb      1010:        if (cf->cf_type == DT_DIR) {
                   1011:                if (cf->cf_root != NULL)
1.119     joris    1012:                        cvsroot_remove(cf->cf_root);
1.62      jfb      1013:                if (cf->cf_repo != NULL)
1.132     joris    1014:                        xfree(cf->cf_repo);
1.62      jfb      1015:                while (!SIMPLEQ_EMPTY(&(cf->cf_files))) {
                   1016:                        child = SIMPLEQ_FIRST(&(cf->cf_files));
                   1017:                        SIMPLEQ_REMOVE_HEAD(&(cf->cf_files), cf_list);
                   1018:                        cvs_file_free(child);
                   1019:                }
1.64      joris    1020:        } else {
                   1021:                if (cf->cf_tag != NULL)
1.132     joris    1022:                        xfree(cf->cf_tag);
1.77      jfb      1023:                if (cf->cf_opts != NULL)
1.132     joris    1024:                        xfree(cf->cf_opts);
1.62      jfb      1025:        }
1.64      joris    1026:
1.132     joris    1027:        xfree(cf);
1.5       jfb      1028: }
                   1029:
1.3       jfb      1030:
                   1031: /*
                   1032:  * cvs_file_sort()
                   1033:  *
1.16      jfb      1034:  * Sort a list of cvs file structures according to their filename.  The list
                   1035:  * <flp> is modified according to the sorting algorithm.  The number of files
                   1036:  * in the list must be given by <nfiles>.
                   1037:  * Returns 0 on success, or -1 on failure.
1.3       jfb      1038:  */
                   1039: static int
1.16      jfb      1040: cvs_file_sort(struct cvs_flist *flp, u_int nfiles)
1.3       jfb      1041: {
                   1042:        int i;
                   1043:        size_t nb;
1.16      jfb      1044:        CVSFILE *cf, **cfvec;
                   1045:
1.136     ray      1046:        cfvec = xcalloc((size_t)nfiles, sizeof(*cfvec));
1.3       jfb      1047:
                   1048:        i = 0;
1.62      jfb      1049:        SIMPLEQ_FOREACH(cf, flp, cf_list) {
1.16      jfb      1050:                if (i == (int)nfiles) {
1.3       jfb      1051:                        cvs_log(LP_WARN, "too many files to sort");
1.16      jfb      1052:                        /* rebuild the list and abort sorting */
                   1053:                        while (--i >= 0)
1.62      jfb      1054:                                SIMPLEQ_INSERT_HEAD(flp, cfvec[i], cf_list);
1.132     joris    1055:                        xfree(cfvec);
1.3       jfb      1056:                        return (-1);
                   1057:                }
1.16      jfb      1058:                cfvec[i++] = cf;
1.3       jfb      1059:
                   1060:                /* now unlink it from the list,
                   1061:                 * we'll put it back in order later
                   1062:                 */
1.62      jfb      1063:                SIMPLEQ_REMOVE_HEAD(flp, cf_list);
1.3       jfb      1064:        }
                   1065:
                   1066:        /* clear the list just in case */
1.62      jfb      1067:        SIMPLEQ_INIT(flp);
1.3       jfb      1068:        nb = (size_t)i;
                   1069:
                   1070:        heapsort(cfvec, nb, sizeof(cf), cvs_file_cmp);
                   1071:
                   1072:        /* rebuild the list from the bottom up */
                   1073:        for (i = (int)nb - 1; i >= 0; i--)
1.62      jfb      1074:                SIMPLEQ_INSERT_HEAD(flp, cfvec[i], cf_list);
1.3       jfb      1075:
1.132     joris    1076:        xfree(cfvec);
1.3       jfb      1077:        return (0);
                   1078: }
                   1079:
                   1080:
                   1081: static int
                   1082: cvs_file_cmp(const void *f1, const void *f2)
                   1083: {
1.41      jfb      1084:        const CVSFILE *cf1, *cf2;
1.62      jfb      1085:        cf1 = *(CVSFILE * const *)f1;
                   1086:        cf2 = *(CVSFILE * const *)f2;
1.99      joris    1087:        return cvs_file_cmpname(cf1->cf_name, cf2->cf_name);
1.6       jfb      1088: }
                   1089:
                   1090:
1.34      jfb      1091: /*
                   1092:  * cvs_file_alloc()
                   1093:  *
                   1094:  * Allocate a CVSFILE structure and initialize its internals.
                   1095:  */
1.109     xsa      1096: CVSFILE *
1.6       jfb      1097: cvs_file_alloc(const char *path, u_int type)
                   1098: {
                   1099:        CVSFILE *cfp;
1.100     joris    1100:        char *p;
1.6       jfb      1101:
1.136     ray      1102:        cfp = xcalloc(1, sizeof(*cfp));
1.6       jfb      1103:
1.62      jfb      1104:        cfp->cf_type = type;
                   1105:        cfp->cf_cvstat = CVS_FST_UNKNOWN;
                   1106:
                   1107:        if (type == DT_DIR) {
                   1108:                SIMPLEQ_INIT(&(cfp->cf_files));
                   1109:        }
                   1110:
1.132     joris    1111:        cfp->cf_name = xstrdup(basename(path));
1.100     joris    1112:        if ((p = strrchr(path, '/')) != NULL) {
                   1113:                *p = '\0';
1.132     joris    1114:                if (strcmp(path, "."))
                   1115:                        cfp->cf_dir = xstrdup(path);
                   1116:                else
1.100     joris    1117:                        cfp->cf_dir = NULL;
                   1118:                *p = '/';
                   1119:        } else
                   1120:                cfp->cf_dir = NULL;
                   1121:
1.6       jfb      1122:        return (cfp);
1.1       jfb      1123: }
1.14      jfb      1124:
                   1125:
                   1126: /*
                   1127:  * cvs_file_lget()
                   1128:  *
                   1129:  * Get the file and link it with the parent right away.
1.22      jfb      1130:  * Returns a pointer to the created file structure on success, or NULL on
                   1131:  * failure.
1.14      jfb      1132:  */
1.109     xsa      1133: static CVSFILE *
1.115     joris    1134: cvs_file_lget(const char *path, int flags, CVSFILE *parent, CVSENTRIES *pent,
                   1135:     struct cvs_ent *ent)
1.14      jfb      1136: {
1.128     joris    1137:        char *c;
1.100     joris    1138:        int ret;
1.44      jfb      1139:        u_int type;
1.14      jfb      1140:        struct stat st;
                   1141:        CVSFILE *cfp;
1.128     joris    1142:        struct cvsroot *root;
1.14      jfb      1143:
1.44      jfb      1144:        type = DT_UNKNOWN;
                   1145:        ret = stat(path, &st);
                   1146:        if (ret == 0)
                   1147:                type = IFTODT(st.st_mode);
1.14      jfb      1148:
1.137   ! deraadt  1149:        if ((flags & CF_REPO) && type != DT_DIR) {
1.128     joris    1150:                if ((c = strrchr(path, ',')) == NULL)
                   1151:                        return (NULL);
                   1152:                *c = '\0';
                   1153:        }
                   1154:
1.44      jfb      1155:        if ((cfp = cvs_file_alloc(path, type)) == NULL)
1.14      jfb      1156:                return (NULL);
                   1157:        cfp->cf_parent = parent;
1.115     joris    1158:        cfp->cf_entry = pent;
1.54      joris    1159:
1.137   ! deraadt  1160:        if (cfp->cf_type == DT_DIR && cfp->cf_parent == NULL)
1.62      jfb      1161:                cfp->cf_flags |= CVS_DIRF_BASE;
1.14      jfb      1162:
1.44      jfb      1163:        if (ret == 0) {
                   1164:                cfp->cf_mode = st.st_mode & ACCESSPERMS;
1.62      jfb      1165:                if (cfp->cf_type == DT_REG)
                   1166:                        cfp->cf_mtime = st.st_mtime;
1.100     joris    1167:                cfp->cf_flags |= CVS_FILE_ONDISK;
1.44      jfb      1168:
                   1169:                if (ent == NULL)
1.100     joris    1170:                        if (cfp->cf_flags & CVS_DIRF_BASE)
                   1171:                                cfp->cf_cvstat = CVS_FST_UPTODATE;
                   1172:                        else
                   1173:                                cfp->cf_cvstat = CVS_FST_UNKNOWN;
1.14      jfb      1174:                else {
1.44      jfb      1175:                        /* always show directories as up-to-date */
                   1176:                        if (ent->ce_type == CVS_ENT_DIR)
1.14      jfb      1177:                                cfp->cf_cvstat = CVS_FST_UPTODATE;
1.44      jfb      1178:                        else if (rcsnum_cmp(ent->ce_rev, cvs_addedrev, 2) == 0)
                   1179:                                cfp->cf_cvstat = CVS_FST_ADDED;
                   1180:                        else {
1.130     joris    1181:                                /*
                   1182:                                 * correct st.st_mtime first
                   1183:                                 */
                   1184:                                if ((st.st_mtime =
                   1185:                                    cvs_hack_time(st.st_mtime, 1)) == 0) {
                   1186:                                        cvs_file_free(cfp);
                   1187:                                        return (NULL);
                   1188:                                }
                   1189:
1.44      jfb      1190:                                /* check last modified time */
1.90      joris    1191:                                if (ent->ce_mtime == (time_t)st.st_mtime) {
1.44      jfb      1192:                                        cfp->cf_cvstat = CVS_FST_UPTODATE;
1.90      joris    1193:                                } else {
1.44      jfb      1194:                                        cfp->cf_cvstat = CVS_FST_MODIFIED;
1.90      joris    1195:                                }
1.44      jfb      1196:                        }
1.94      joris    1197:
                   1198:                        cfp->cf_etime = ent->ce_mtime;
1.14      jfb      1199:                }
1.44      jfb      1200:        } else {
                   1201:                if (ent == NULL) {
1.92      joris    1202:                        /* assume it is a file and unknown */
                   1203:                        cfp->cf_cvstat = CVS_FST_UNKNOWN;
                   1204:                        cfp->cf_type = DT_REG;
1.66      joris    1205:                } else {
                   1206:                        if (ent->ce_type == CVS_ENT_FILE)
                   1207:                                cfp->cf_type = DT_REG;
                   1208:                        else if (ent->ce_type == CVS_ENT_DIR)
                   1209:                                cfp->cf_type = DT_DIR;
                   1210:                        else
1.93      joris    1211:                                cvs_log(LP_WARN, "unknown ce_type %d",
1.66      joris    1212:                                    ent->ce_type);
1.67      joris    1213:
                   1214:                        if (ent->ce_status == CVS_ENT_REMOVED)
                   1215:                                cfp->cf_cvstat = CVS_FST_REMOVED;
1.90      joris    1216:                        else if (ent->ce_status == CVS_ENT_UPTODATE)
                   1217:                                cfp->cf_cvstat = CVS_FST_UPTODATE;
1.96      joris    1218:                        else if (ent->ce_status == CVS_ENT_ADDED)
                   1219:                                cfp->cf_cvstat = CVS_FST_ADDED;
1.67      joris    1220:                        else
                   1221:                                cfp->cf_cvstat = CVS_FST_LOST;
1.66      joris    1222:                }
1.128     joris    1223:
                   1224:                /* XXX assume 0644 ? */
                   1225:                cfp->cf_mode = 0644;
1.14      jfb      1226:        }
1.52      jfb      1227:
1.62      jfb      1228:        if (ent != NULL) {
                   1229:                /* steal the RCSNUM */
                   1230:                cfp->cf_lrev = ent->ce_rev;
1.72      jfb      1231:
1.77      jfb      1232:                if (ent->ce_type == CVS_ENT_FILE) {
1.132     joris    1233:                        if (ent->ce_tag[0] != '\0')
                   1234:                                cfp->cf_tag = xstrdup(ent->ce_tag);
1.77      jfb      1235:
1.132     joris    1236:                        if (ent->ce_opts[0] != '\0')
                   1237:                                cfp->cf_opts = xstrdup(ent->ce_opts);
1.62      jfb      1238:                }
                   1239:        }
1.14      jfb      1240:
1.115     joris    1241:        if (cfp->cf_type == DT_DIR) {
                   1242:                if (cvs_load_dirinfo(cfp, flags) < 0) {
                   1243:                        cvs_file_free(cfp);
                   1244:                        return (NULL);
                   1245:                }
1.14      jfb      1246:        }
1.74      joris    1247:
1.128     joris    1248:        if (flags & CF_REPO) {
                   1249:                root = CVS_DIR_ROOT(cfp);
                   1250:
                   1251:                cfp->cf_mode = 0644;
                   1252:                cfp->cf_cvstat = CVS_FST_LOST;
                   1253:
1.132     joris    1254:                c = xstrdup(cfp->cf_dir);
                   1255:                xfree(cfp->cf_dir);
1.128     joris    1256:
                   1257:                if (strcmp(c, root->cr_dir)) {
                   1258:                        c += strlen(root->cr_dir) + 1;
1.132     joris    1259:                        cfp->cf_dir = xstrdup(c);
1.128     joris    1260:                        c -= strlen(root->cr_dir) + 1;
                   1261:                } else {
                   1262:                        cfp->cf_dir = NULL;
                   1263:                }
                   1264:
1.132     joris    1265:                xfree(c);
1.128     joris    1266:        }
                   1267:
1.137   ! deraadt  1268:        if (cfp->cf_repo != NULL && cfp->cf_type == DT_DIR &&
1.74      joris    1269:            !strcmp(cfp->cf_repo, path))
                   1270:                cfp->cf_cvstat = CVS_FST_UPTODATE;
1.14      jfb      1271:
1.100     joris    1272:        /*
                   1273:         * In server mode, we do a few extra checks.
                   1274:         */
                   1275:        if (cvs_cmdop == CVS_OP_SERVER) {
                   1276:                /*
                   1277:                 * If for some reason a file was added,
                   1278:                 * but does not exist anymore, start complaining.
                   1279:                 */
                   1280:                if (!(cfp->cf_flags & CVS_FILE_ONDISK) &&
1.124     reyk     1281:                    (cfp->cf_cvstat == CVS_FST_ADDED) &&
1.100     joris    1282:                    (cfp->cf_type != DT_DIR))
                   1283:                        cvs_log(LP_WARN, "new-born %s has disappeared", path);
                   1284:
                   1285:                /*
                   1286:                 * Any other needed checks?
                   1287:                 */
                   1288:        }
                   1289:
1.14      jfb      1290:        return (cfp);
1.23      jfb      1291: }
                   1292:
                   1293:
                   1294: static int
                   1295: cvs_file_cmpname(const char *name1, const char *name2)
                   1296: {
                   1297:        return (cvs_nocase == 0) ? (strcmp(name1, name2)) :
                   1298:            (strcasecmp(name1, name2));
1.88      joris    1299: }
                   1300:
                   1301: /*
1.100     joris    1302:  * remove any empty directories.
1.88      joris    1303:  */
                   1304: int
                   1305: cvs_file_prune(char *path)
                   1306: {
                   1307:        DIR *dirp;
                   1308:        int l, pwd, empty;
                   1309:        struct dirent *dp;
                   1310:        char fpath[MAXPATHLEN];
                   1311:        CVSENTRIES *entf;
1.89      joris    1312:        CVSFILE *cfp;
1.88      joris    1313:
                   1314:        pwd = (!strcmp(path, "."));
                   1315:
                   1316:        if ((dirp = opendir(path)) == NULL) {
                   1317:                cvs_log(LP_ERRNO, "failed to open `%s'", fpath);
                   1318:                return (-1);
                   1319:        }
                   1320:
                   1321:        empty = 0;
                   1322:        entf = cvs_ent_open(path, O_RDWR);
                   1323:
                   1324:        while ((dp = readdir(dirp)) != NULL) {
                   1325:                if (!strcmp(dp->d_name, ".") ||
                   1326:                    !strcmp(dp->d_name, "..") ||
                   1327:                    !strcmp(dp->d_name, CVS_PATH_CVSDIR))
                   1328:                        continue;
                   1329:
                   1330:                empty++;
                   1331:                if (dp->d_type == DT_DIR) {
                   1332:                        l = snprintf(fpath, sizeof(fpath), "%s%s%s",
                   1333:                            (pwd) ? "" : path, (pwd) ? "" : "/", dp->d_name);
                   1334:                        if (l == -1 || l >= (int)sizeof(fpath)) {
                   1335:                                errno = ENAMETOOLONG;
                   1336:                                cvs_log(LP_ERRNO, "%s", fpath);
                   1337:                                continue;
                   1338:                        }
1.89      joris    1339:
                   1340:                        cfp = cvs_file_find(cvs_files, fpath);
                   1341:                        if (cfp == NULL)
1.91      joris    1342:                                continue;
                   1343:
                   1344:                        /* ignore unknown directories */
                   1345:                        if (cfp->cf_cvstat == CVS_FST_UNKNOWN)
1.89      joris    1346:                                continue;
1.88      joris    1347:
                   1348:                        if (cvs_file_prune(fpath)) {
                   1349:                                empty--;
                   1350:                                if (entf)
1.128     joris    1351:                                        cvs_ent_remove(entf, fpath, 0);
1.88      joris    1352:                        } else {
                   1353:                                empty++;
                   1354:                        }
                   1355:                }
                   1356:        }
                   1357:
                   1358:        closedir(dirp);
                   1359:        if (entf)
                   1360:                cvs_ent_close(entf);
                   1361:
                   1362:        empty = (empty == 0);
                   1363:        if (empty) {
1.106     xsa      1364:                if (cvs_rmdir(path) < 0) {
1.88      joris    1365:                        cvs_log(LP_ERR, "failed to prune `%s'", path);
                   1366:                        empty = 0;
                   1367:                }
1.93      joris    1368:        }
1.88      joris    1369:
                   1370:        return (empty);
1.14      jfb      1371: }