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: }