[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.3

1.1       jfb         1: /*     $OpenBSD$       */
                      2: /*
                      3:  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
                      4:  * All rights reserved.
                      5:  *
                      6:  * Redistribution and use in source and binary forms, with or without
                      7:  * modification, are permitted provided that the following conditions
                      8:  * are met:
                      9:  *
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. The name of the author may not be used to endorse or promote products
                     13:  *    derived from this software without specific prior written permission.
                     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
                     24:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     25:  */
                     26:
                     27: #include <sys/types.h>
                     28: #include <sys/queue.h>
                     29: #include <sys/stat.h>
                     30:
                     31: #include <pwd.h>
                     32: #include <errno.h>
                     33: #include <stdio.h>
                     34: #include <fcntl.h>
                     35: #include <dirent.h>
                     36: #include <stdlib.h>
                     37: #include <unistd.h>
                     38: #include <string.h>
                     39: #include <fnmatch.h>
                     40:
                     41: #include "cvs.h"
                     42: #include "log.h"
                     43:
                     44:
                     45: #define CVS_IGN_STATIC    0x01     /* pattern is static, no need to glob */
                     46:
                     47:
                     48:
                     49: #define CVS_CHAR_ISMETA(c)  ((c == '*') || (c == '?') || (c == '['))
                     50:
                     51:
                     52:
                     53: /* ignore pattern */
                     54: struct cvs_ignpat {
                     55:        char  ip_pat[MAXNAMLEN];
                     56:        int   ip_flags;
                     57:        TAILQ_ENTRY (cvs_ignpat) ip_list;
                     58: };
                     59:
                     60:
                     61: /*
                     62:  * Standard patterns to ignore.
                     63:  */
                     64:
                     65: static const char *cvs_ign_std[] = {
                     66:        ".",
                     67:        "..",
                     68:        "*.o",
                     69:        "*.so",
                     70:        "*.bak",
                     71:        "*.orig",
                     72:        "*.rej",
                     73:        "*.exe",
                     74:        "*.depend",
                     75:        "CVS",
                     76:        "core",
                     77: #ifdef OLD_SMELLY_CRUFT
                     78:        "RCSLOG",
                     79:        "tags",
                     80:        "TAGS",
                     81:        "RCS",
                     82:        "SCCS",
                     83:        "#*",
                     84:        ".#*",
                     85:        ",*",
                     86: #endif
                     87: };
                     88:
                     89:
                     90: TAILQ_HEAD(, cvs_ignpat)  cvs_ign_pats;
                     91:
                     92:
1.3     ! jfb        93: static struct cvs_dir*  cvs_file_getdir  (struct cvs_file *, int);
        !            94: static void             cvs_file_freedir (struct cvs_dir *);
        !            95: static int              cvs_file_sort    (struct cvs_flist *);
        !            96: static int              cvs_file_cmp     (const void *, const void *);
        !            97:
        !            98:
        !            99:
1.1       jfb       100: /*
                    101:  * cvs_file_init()
                    102:  *
                    103:  */
                    104:
                    105: int
                    106: cvs_file_init(void)
                    107: {
                    108:        int i;
                    109:        size_t len;
                    110:        char path[MAXPATHLEN], buf[MAXNAMLEN];
                    111:        FILE *ifp;
                    112:        struct passwd *pwd;
                    113:
                    114:        TAILQ_INIT(&cvs_ign_pats);
                    115:
                    116:        /* standard patterns to ignore */
                    117:        for (i = 0; i < sizeof(cvs_ign_std)/sizeof(char *); i++)
                    118:                cvs_file_ignore(cvs_ign_std[i]);
                    119:
                    120:        /* read the cvsignore file in the user's home directory, if any */
                    121:        pwd = getpwuid(getuid());
                    122:        if (pwd != NULL) {
                    123:                snprintf(path, sizeof(path), "%s/.cvsignore", pwd->pw_dir);
                    124:                ifp = fopen(path, "r");
                    125:                if (ifp == NULL) {
                    126:                        if (errno != ENOENT)
                    127:                                cvs_log(LP_ERRNO, "failed to open `%s'", path);
                    128:                }
                    129:                else {
                    130:                        while (fgets(buf, sizeof(buf), ifp) != NULL) {
                    131:                                len = strlen(buf);
                    132:                                if (len == 0)
                    133:                                        continue;
                    134:                                if (buf[len - 1] != '\n') {
                    135:                                        cvs_log(LP_ERR, "line too long in `%s'",
                    136:                                            path);
                    137:                                }
                    138:                                buf[--len] = '\0';
                    139:                                cvs_file_ignore(buf);
                    140:                        }
                    141:                        (void)fclose(ifp);
                    142:                }
                    143:        }
                    144:
                    145:        return (0);
                    146: }
                    147:
                    148:
                    149: /*
                    150:  * cvs_file_ignore()
                    151:  *
                    152:  * Add the pattern <pat> to the list of patterns for files to ignore.
                    153:  * Returns 0 on success, or -1 on failure.
                    154:  */
                    155:
                    156: int
                    157: cvs_file_ignore(const char *pat)
                    158: {
                    159:        char *cp;
                    160:        struct cvs_ignpat *ip;
                    161:
                    162:        ip = (struct cvs_ignpat *)malloc(sizeof(*ip));
                    163:        if (ip == NULL) {
                    164:                cvs_log(LP_ERR, "failed to allocate space for ignore pattern");
                    165:                return (-1);
                    166:        }
                    167:
                    168:        strlcpy(ip->ip_pat, pat, sizeof(ip->ip_pat));
                    169:
                    170:        /* check if we will need globbing for that pattern */
                    171:        ip->ip_flags = CVS_IGN_STATIC;
                    172:        for (cp = ip->ip_pat; *cp != '\0'; cp++) {
                    173:                if (CVS_CHAR_ISMETA(*cp)) {
                    174:                        ip->ip_flags &= ~CVS_IGN_STATIC;
                    175:                        break;
                    176:                }
                    177:        }
                    178:
                    179:        TAILQ_INSERT_TAIL(&cvs_ign_pats, ip, ip_list);
                    180:
                    181:        return (0);
                    182: }
                    183:
                    184:
                    185: /*
                    186:  * cvs_file_isignored()
                    187:  *
                    188:  * Returns 1 if the filename <file> is matched by one of the ignore
                    189:  * patterns, or 0 otherwise.
                    190:  */
                    191:
                    192: int
                    193: cvs_file_isignored(const char *file)
                    194: {
                    195:        struct cvs_ignpat *ip;
                    196:
                    197:        TAILQ_FOREACH(ip, &cvs_ign_pats, ip_list) {
                    198:                if (ip->ip_flags & CVS_IGN_STATIC) {
                    199:                        if (strcmp(file, ip->ip_pat) == 0)
                    200:                                return (1);
                    201:                }
                    202:                else if (fnmatch(ip->ip_pat, file, FNM_PERIOD) == 0)
                    203:                        return (1);
                    204:        }
                    205:
                    206:        return (0);
                    207: }
                    208:
                    209:
                    210: /*
                    211:  * cvs_file_getv()
                    212:  *
                    213:  * Get a vector of all the files found in the directory <dir> and not
                    214:  * matching any of the ignore patterns.  The number of files found is
                    215:  * returned in <nfiles>.
                    216:  * Returns a pointer to a dynamically-allocated string vector on success,
1.2       jfb       217:  * or NULL on failure.  The returned vector should be freed with
                    218:  * cvs_freeargv().
1.1       jfb       219:  */
                    220:
                    221: char**
1.3     ! jfb       222: cvs_file_getv(const char *dir, int *nfiles, int recurse)
1.1       jfb       223: {
                    224:        int nf, ret, fd;
                    225:        long base;
                    226:        void *dp, *ep, *tmp;
                    227:        char fbuf[1024], **fvec;
                    228:        struct dirent *ent;
                    229:
                    230:        *nfiles = 0;
                    231:        fvec = NULL;
                    232:
                    233:        fd = open(dir, O_RDONLY);
                    234:        if (fd == -1) {
                    235:                cvs_log(LP_ERRNO, "failed to open `%s'", dir);
                    236:                return (NULL);
                    237:        }
                    238:        ret = getdirentries(fd, fbuf, sizeof(fbuf), &base);
                    239:        if (ret == -1) {
                    240:                cvs_log(LP_ERRNO, "failed to get directory entries");
                    241:                (void)close(fd);
                    242:                return (NULL);
                    243:        }
                    244:
                    245:        dp = fbuf;
                    246:        ep = fbuf + (size_t)ret;
                    247:        while (dp < ep) {
                    248:                ent = (struct dirent *)dp;
                    249:                dp += ent->d_reclen;
                    250:
                    251:                if (cvs_file_isignored(ent->d_name))
                    252:                        continue;
                    253:
                    254:                tmp = realloc(fvec, (*nfiles + 1) * sizeof(char *));
                    255:                if (tmp == NULL) {
                    256:                        cvs_log(LP_ERRNO, "failed to reallocate file vector");
                    257:                        (void)close(fd);
                    258:                        free(fvec);
                    259:                        return (NULL);
                    260:                }
                    261:                fvec[++(*nfiles)] = strdup(ent->d_name);
                    262:
                    263:                *nfiles++;
                    264:        }
                    265:
                    266:        (void)close(fd);
                    267:
                    268:        return (fvec);
1.3     ! jfb       269: }
        !           270:
        !           271:
        !           272: /*
        !           273:  * cvs_file_get()
        !           274:  *
        !           275:  * Load a cvs_file structure with all the information pertaining to the file
        !           276:  * <path>.
        !           277:  * Returns a pointer to the cvs file structure, which must later be freed
        !           278:  * with cvs_file_free().
        !           279:  */
        !           280:
        !           281: struct cvs_file*
        !           282: cvs_file_get(const char *path, int flags)
        !           283: {
        !           284:        size_t dlen;
        !           285:        struct stat st;
        !           286:        struct cvs_file *cfp;
        !           287:
        !           288:        printf("cvs_file_get(%s)\n", path);
        !           289:
        !           290:        if (stat(path, &st) == -1) {
        !           291:                cvs_log(LP_ERRNO, "failed to stat file");
        !           292:                return (NULL);
        !           293:        }
        !           294:
        !           295:        cfp = (struct cvs_file *)malloc(sizeof(*cfp));
        !           296:        if (cfp == NULL) {
        !           297:                cvs_log(LP_ERRNO, "failed to allocate CVS file data");
        !           298:                return (NULL);
        !           299:        }
        !           300:        memset(cfp, 0, sizeof(*cfp));
        !           301:
        !           302:        cfp->cf_path = strdup(path);
        !           303:        if (cfp->cf_path == NULL) {
        !           304:                free(cfp);
        !           305:                return (NULL);
        !           306:        }
        !           307:
        !           308:        cfp->cf_name = strrchr(cfp->cf_path, '/');
        !           309:        if (cfp->cf_name == NULL)
        !           310:                cfp->cf_name = cfp->cf_path;
        !           311:        else
        !           312:                cfp->cf_name++;
        !           313:
        !           314:        /* convert from stat mode to dirent values */
        !           315:        cfp->cf_type = IFTODT(st.st_mode);
        !           316:        if (cfp->cf_type == DT_DIR) {
        !           317:                cfp->cf_ddat = cvs_file_getdir(cfp, flags);
        !           318:                if (cfp->cf_ddat == NULL) {
        !           319:                        cvs_file_free(cfp);
        !           320:                        return (NULL);
        !           321:                }
        !           322:        }
        !           323:
        !           324:        if (flags & CF_STAT) {
        !           325:                cfp->cf_stat = (struct stat *)malloc(sizeof(struct stat));
        !           326:                if (cfp->cf_stat == NULL) {
        !           327:                        cvs_log(LP_ERRNO, "failed to allocate stat structure");
        !           328:                        cvs_file_free(cfp);
        !           329:                        return (NULL);
        !           330:                }
        !           331:
        !           332:                memcpy(cfp->cf_stat, &st, sizeof(struct stat));
        !           333:        }
        !           334:
        !           335:        return (cfp);
        !           336: }
        !           337:
        !           338:
        !           339: /*
        !           340:  * cvs_file_getdir()
        !           341:  *
        !           342:  * Get a cvs directory structure for the directory whose path is <dir>.
        !           343:  */
        !           344:
        !           345: static struct cvs_dir*
        !           346: cvs_file_getdir(struct cvs_file *cf, int flags)
        !           347: {
        !           348:        int nf, ret, fd;
        !           349:        long base;
        !           350:        void *dp, *ep, *tmp;
        !           351:        char fbuf[1024], pbuf[MAXPATHLEN];
        !           352:        struct dirent *ent;
        !           353:        struct cvs_file *cfp;
        !           354:        struct cvs_dir *cdp;
        !           355:
        !           356:        cdp = (struct cvs_dir *)malloc(sizeof(*cdp));
        !           357:        if (cdp == NULL) {
        !           358:                cvs_log(LP_ERRNO, "failed to allocate dir");
        !           359:                return (NULL);
        !           360:        }
        !           361:        memset(cdp, 0, sizeof(*cdp));
        !           362:        LIST_INIT(&(cdp->cd_files));
        !           363:
        !           364:        if (cvs_readrepo(cf->cf_path, pbuf, sizeof(pbuf)) < 0) {
        !           365:                free(cdp);
        !           366:                return (NULL);
        !           367:        }
        !           368:
        !           369:        cdp->cd_repo = strdup(pbuf);
        !           370:        if (cdp->cd_repo == NULL) {
        !           371:                free(cdp);
        !           372:                return (NULL);
        !           373:        }
        !           374:
        !           375:        cdp->cd_root = cvsroot_get(cf->cf_path);
        !           376:        if (cdp->cd_root == NULL) {
        !           377:                cvs_file_freedir(cdp);
        !           378:                return (NULL);
        !           379:        }
        !           380:
        !           381:        fd = open(cf->cf_path, O_RDONLY);
        !           382:        if (fd == -1) {
        !           383:                cvs_log(LP_ERRNO, "failed to open `%s'", cf->cf_path);
        !           384:                cvs_file_freedir(cdp);
        !           385:                return (NULL);
        !           386:        }
        !           387:        ret = getdirentries(fd, fbuf, sizeof(fbuf), &base);
        !           388:        if (ret == -1) {
        !           389:                cvs_log(LP_ERRNO, "failed to get directory entries");
        !           390:                (void)close(fd);
        !           391:                cvs_file_freedir(cdp);
        !           392:                return (NULL);
        !           393:        }
        !           394:
        !           395:        dp = fbuf;
        !           396:        ep = fbuf + (size_t)ret;
        !           397:        while (dp < ep) {
        !           398:                ent = (struct dirent *)dp;
        !           399:                dp += ent->d_reclen;
        !           400:
        !           401:                if ((flags & CF_IGNORE) && cvs_file_isignored(ent->d_name))
        !           402:                        continue;
        !           403:
        !           404:                snprintf(pbuf, sizeof(pbuf), "%s/%s", cf->cf_path, ent->d_name);
        !           405:                cfp = cvs_file_get(pbuf, flags);
        !           406:
        !           407:                LIST_INSERT_HEAD(&(cdp->cd_files), cfp, cf_list);
        !           408:        }
        !           409:
        !           410:        if (flags & CF_SORT)
        !           411:                cvs_file_sort(&(cdp->cd_files));
        !           412:
        !           413:        (void)close(fd);
        !           414:
        !           415:        return (cdp);
        !           416: }
        !           417:
        !           418:
        !           419: /*
        !           420:  * cvs_file_free()
        !           421:  *
        !           422:  * Free a cvs_file structure and its contents.
        !           423:  */
        !           424:
        !           425: void
        !           426: cvs_file_free(struct cvs_file *cf)
        !           427: {
        !           428:        struct cvs_file *cfp;
        !           429:        struct cvs_dir *cd;
        !           430:
        !           431:        if (cf->cf_path != NULL)
        !           432:                free(cf->cf_path);
        !           433:        if (cf->cf_stat != NULL)
        !           434:                free(cf->cf_stat);
        !           435:        if (cf->cf_ddat != NULL)
        !           436:                cvs_file_freedir(cf->cf_ddat);
        !           437:        free(cf);
        !           438: }
        !           439:
        !           440:
        !           441: /*
        !           442:  * cvs_file_freedir()
        !           443:  *
        !           444:  * Free a cvs_dir structure and its contents.
        !           445:  */
        !           446:
        !           447: static void
        !           448: cvs_file_freedir(struct cvs_dir *cd)
        !           449: {
        !           450:        struct cvs_file *cfp;
        !           451:
        !           452:        if (cd->cd_root != NULL)
        !           453:                cvsroot_free(cd->cd_root);
        !           454:        if (cd->cd_repo != NULL)
        !           455:                free(cd->cd_repo);
        !           456:
        !           457:        while (!LIST_EMPTY(&(cd->cd_files))) {
        !           458:                cfp = LIST_FIRST(&(cd->cd_files));
        !           459:                LIST_REMOVE(cfp, cf_list);
        !           460:                cvs_file_free(cfp);
        !           461:        }
        !           462: }
        !           463:
        !           464:
        !           465: /*
        !           466:  * cvs_file_sort()
        !           467:  *
        !           468:  * Sort a list of cvs file structures according to their filename.
        !           469:  */
        !           470:
        !           471: static int
        !           472: cvs_file_sort(struct cvs_flist *flp)
        !           473: {
        !           474:        int i;
        !           475:        size_t nb;
        !           476:        struct cvs_file *cf, *cfvec[256];
        !           477:
        !           478:        i = 0;
        !           479:        LIST_FOREACH(cf, flp, cf_list) {
        !           480:                printf("adding `%s'\n", cf->cf_path);
        !           481:                cfvec[i++] = cf;
        !           482:                if (i == sizeof(cfvec)/sizeof(struct cvs_file *)) {
        !           483:                        cvs_log(LP_WARN, "too many files to sort");
        !           484:                        return (-1);
        !           485:                }
        !           486:
        !           487:                /* now unlink it from the list,
        !           488:                 * we'll put it back in order later
        !           489:                 */
        !           490:                LIST_REMOVE(cf, cf_list);
        !           491:        }
        !           492:
        !           493:        /* clear the list just in case */
        !           494:        LIST_INIT(flp);
        !           495:        nb = (size_t)i;
        !           496:
        !           497:        printf("Before: \n");
        !           498:        for (i = 0; i < (int)nb; i++)
        !           499:                printf("[%d] = `%s'\n", i, cfvec[i]->cf_name);
        !           500:        heapsort(cfvec, nb, sizeof(cf), cvs_file_cmp);
        !           501:        printf("===================================\nAfter: \n");
        !           502:        for (i = 0; i < (int)nb; i++)
        !           503:                printf("[%d] = `%s'\n", i, cfvec[i]->cf_name);
        !           504:
        !           505:        /* rebuild the list from the bottom up */
        !           506:        for (i = (int)nb - 1; i >= 0; i--)
        !           507:                LIST_INSERT_HEAD(flp, cfvec[i], cf_list);
        !           508:
        !           509:        return (0);
        !           510: }
        !           511:
        !           512:
        !           513: static int
        !           514: cvs_file_cmp(const void *f1, const void *f2)
        !           515: {
        !           516:        struct cvs_file *cf1, *cf2;
        !           517:        cf1 = *(struct cvs_file **)f1;
        !           518:        cf2 = *(struct cvs_file **)f2;
        !           519:        return strcmp(cf1->cf_name, cf2->cf_name);
1.1       jfb       520: }