du.c revision 128834
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1989, 1993, 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Chris Newcomb.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 3. All advertising materials mentioning features or use of this software
171590Srgrimes *    must display the following acknowledgement:
181590Srgrimes *	This product includes software developed by the University of
191590Srgrimes *	California, Berkeley and its contributors.
201590Srgrimes * 4. Neither the name of the University nor the names of its contributors
211590Srgrimes *    may be used to endorse or promote products derived from this software
221590Srgrimes *    without specific prior written permission.
231590Srgrimes *
241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341590Srgrimes * SUCH DAMAGE.
351590Srgrimes */
361590Srgrimes
371590Srgrimes#ifndef lint
3841568Sarchiestatic const char copyright[] =
391590Srgrimes"@(#) Copyright (c) 1989, 1993, 1994\n\
401590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411590Srgrimes#endif /* not lint */
421590Srgrimes
431590Srgrimes#ifndef lint
4456597Smharo#if 0
4541568Sarchiestatic const char sccsid[] = "@(#)du.c	8.5 (Berkeley) 5/4/95";
4656597Smharo#endif
471590Srgrimes#endif /* not lint */
4899112Sobrien#include <sys/cdefs.h>
4999112Sobrien__FBSDID("$FreeBSD: head/usr.bin/du/du.c 128834 2004-05-02 17:54:57Z kientzle $");
501590Srgrimes
511590Srgrimes#include <sys/param.h>
5278158Sroam#include <sys/queue.h>
531590Srgrimes#include <sys/stat.h>
541590Srgrimes
551590Srgrimes#include <err.h>
561590Srgrimes#include <errno.h>
5778158Sroam#include <fnmatch.h>
581590Srgrimes#include <fts.h>
5956597Smharo#include <math.h>
601590Srgrimes#include <stdio.h>
611590Srgrimes#include <stdlib.h>
621590Srgrimes#include <string.h>
6356597Smharo#include <sysexits.h>
6423693Speter#include <unistd.h>
651590Srgrimes
6656597Smharo#define	KILO_SZ(n) (n)
6756597Smharo#define	MEGA_SZ(n) ((n) * (n))
6856597Smharo#define	GIGA_SZ(n) ((n) * (n) * (n))
6956597Smharo#define	TERA_SZ(n) ((n) * (n) * (n) * (n))
7056597Smharo#define	PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
7156597Smharo
7256597Smharo#define	KILO_2_SZ (KILO_SZ(1024ULL))
7356597Smharo#define	MEGA_2_SZ (MEGA_SZ(1024ULL))
7456597Smharo#define	GIGA_2_SZ (GIGA_SZ(1024ULL))
7556597Smharo#define	TERA_2_SZ (TERA_SZ(1024ULL))
7656597Smharo#define	PETA_2_SZ (PETA_SZ(1024ULL))
7756597Smharo
7856597Smharo#define	KILO_SI_SZ (KILO_SZ(1000ULL))
7956597Smharo#define	MEGA_SI_SZ (MEGA_SZ(1000ULL))
8056597Smharo#define	GIGA_SI_SZ (GIGA_SZ(1000ULL))
8156597Smharo#define	TERA_SI_SZ (TERA_SZ(1000ULL))
8256597Smharo#define	PETA_SI_SZ (PETA_SZ(1000ULL))
8356597Smharo
8456597Smharounsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
8556597Smharounsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
8656597Smharounsigned long long *valp;
8756597Smharo
8856597Smharotypedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t;
8956597Smharo
9056597Smharoint unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA };
9156597Smharo
9278158SroamSLIST_HEAD(ignhead, ignentry) ignores;
9378158Sroamstruct ignentry {
9478158Sroam	char			*mask;
9578158Sroam	SLIST_ENTRY(ignentry)	next;
9678158Sroam};
9778158Sroam
98128772Skientzlestatic int	linkchk(FTSENT *);
9992920Simpstatic void	usage(void);
10092920Simpvoid		prthumanval(double);
10192920Simpunit_t		unit_adjust(double *);
10292920Simpvoid		ignoreadd(const char *);
10392920Simpvoid		ignoreclean(void);
10492920Simpint		ignorep(FTSENT *);
1051590Srgrimes
1061590Srgrimesint
107100822Sdwmalonemain(int argc, char *argv[])
1081590Srgrimes{
10932097Sjkh	FTS		*fts;
11032097Sjkh	FTSENT		*p;
11137952Sdes	long		blocksize, savednumber = 0;
11232097Sjkh	int		ftsoptions;
11332097Sjkh	int		listall;
11432097Sjkh	int		depth;
115108453Smike	int		Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval;
11632097Sjkh	char 		**save;
11787216Smarkm	static char	dot[] = ".";
1181590Srgrimes
11956597Smharo	Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0;
120128772Skientzle
1211590Srgrimes	save = argv;
12232097Sjkh	ftsoptions = 0;
12319120Sscrappy	depth = INT_MAX;
12478158Sroam	SLIST_INIT(&ignores);
125128772Skientzle
12678158Sroam	while ((ch = getopt(argc, argv, "HI:LPasd:chkrx")) != -1)
1271590Srgrimes		switch (ch) {
12832097Sjkh			case 'H':
12932097Sjkh				Hflag = 1;
13032097Sjkh				break;
13178158Sroam			case 'I':
13278158Sroam				ignoreadd(optarg);
13378158Sroam				break;
13432097Sjkh			case 'L':
13532097Sjkh				if (Pflag)
13632097Sjkh					usage();
13732097Sjkh				Lflag = 1;
13832097Sjkh				break;
13932097Sjkh			case 'P':
14032097Sjkh				if (Lflag)
14132097Sjkh					usage();
14232097Sjkh				Pflag = 1;
14332097Sjkh				break;
14432097Sjkh			case 'a':
14532097Sjkh				aflag = 1;
14632097Sjkh				break;
14732097Sjkh			case 's':
14832097Sjkh				sflag = 1;
14932097Sjkh				break;
15032097Sjkh			case 'd':
15132097Sjkh				dflag = 1;
15232097Sjkh				errno = 0;
15332097Sjkh				depth = atoi(optarg);
15432097Sjkh				if (errno == ERANGE || depth < 0) {
15558601Scharnier					warnx("invalid argument to option d: %s", optarg);
15632097Sjkh					usage();
15732097Sjkh				}
15832097Sjkh				break;
15932097Sjkh			case 'c':
16032097Sjkh				cflag = 1;
16132097Sjkh				break;
16256597Smharo			case 'h':
16356597Smharo				putenv("BLOCKSIZE=512");
16456597Smharo				hflag = 1;
16556597Smharo				valp = vals_base2;
16656597Smharo				break;
16756597Smharo			case 'k':
168112855Sobrien				hflag = 0;
169112855Sobrien				putenv("BLOCKSIZE=1024");
17056597Smharo				break;
17156597Smharo			case 'r':		 /* Compatibility. */
17256597Smharo				break;
17356597Smharo			case 'x':
17456597Smharo				ftsoptions |= FTS_XDEV;
17556597Smharo				break;
17632097Sjkh			case '?':
17732097Sjkh			default:
17819120Sscrappy				usage();
1791590Srgrimes		}
18032097Sjkh
1811590Srgrimes	argc -= optind;
1821590Srgrimes	argv += optind;
1831590Srgrimes
1841590Srgrimes	/*
1851590Srgrimes	 * XXX
1861590Srgrimes	 * Because of the way that fts(3) works, logical walks will not count
1871590Srgrimes	 * the blocks actually used by symbolic links.  We rationalize this by
1881590Srgrimes	 * noting that users computing logical sizes are likely to do logical
1891590Srgrimes	 * copies, so not counting the links is correct.  The real reason is
1901590Srgrimes	 * that we'd have to re-implement the kernel's symbolic link traversing
1911590Srgrimes	 * algorithm to get this right.  If, for example, you have relative
1921590Srgrimes	 * symbolic links referencing other relative symbolic links, it gets
1931590Srgrimes	 * very nasty, very fast.  The bottom line is that it's documented in
1941590Srgrimes	 * the man page, so it's a feature.
1951590Srgrimes	 */
19632097Sjkh
19732097Sjkh	if (Hflag + Lflag + Pflag > 1)
19832097Sjkh		usage();
19932097Sjkh
20032097Sjkh	if (Hflag + Lflag + Pflag == 0)
20132097Sjkh		Pflag = 1;			/* -P (physical) is default */
20232097Sjkh
2031590Srgrimes	if (Hflag)
2041590Srgrimes		ftsoptions |= FTS_COMFOLLOW;
20532097Sjkh
20632097Sjkh	if (Lflag)
2071590Srgrimes		ftsoptions |= FTS_LOGICAL;
2081590Srgrimes
20932097Sjkh	if (Pflag)
21032097Sjkh		ftsoptions |= FTS_PHYSICAL;
21132097Sjkh
21232097Sjkh	listall = 0;
21332097Sjkh
2141590Srgrimes	if (aflag) {
21519120Sscrappy		if (sflag || dflag)
2161590Srgrimes			usage();
21732097Sjkh		listall = 1;
21819120Sscrappy	} else if (sflag) {
21919120Sscrappy		if (dflag)
22019120Sscrappy			usage();
22132097Sjkh		depth = 0;
2221590Srgrimes	}
2231590Srgrimes
2241590Srgrimes	if (!*argv) {
2251590Srgrimes		argv = save;
22687216Smarkm		argv[0] = dot;
2271590Srgrimes		argv[1] = NULL;
2281590Srgrimes	}
2291590Srgrimes
23032097Sjkh	(void) getbsize(&notused, &blocksize);
2311590Srgrimes	blocksize /= 512;
2321590Srgrimes
23332097Sjkh	rval = 0;
234128772Skientzle
2351590Srgrimes	if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
23632097Sjkh		err(1, "fts_open");
2371590Srgrimes
23832097Sjkh	while ((p = fts_read(fts)) != NULL) {
2391590Srgrimes		switch (p->fts_info) {
24032097Sjkh			case FTS_D:			/* Ignore. */
24178158Sroam				if (ignorep(p))
24278158Sroam					fts_set(fts, p, FTS_SKIP);
2431590Srgrimes				break;
24432097Sjkh			case FTS_DP:
24578158Sroam				if (ignorep(p))
24678158Sroam					break;
24778158Sroam
24832097Sjkh				p->fts_parent->fts_number +=
24932097Sjkh				    p->fts_number += p->fts_statp->st_blocks;
250128772Skientzle
25158601Scharnier				if (p->fts_level <= depth) {
25256597Smharo					if (hflag) {
25358522Smharo						(void) prthumanval(howmany(p->fts_number, blocksize));
25456597Smharo						(void) printf("\t%s\n", p->fts_path);
25556597Smharo					} else {
25632097Sjkh					(void) printf("%ld\t%s\n",
25732097Sjkh					    howmany(p->fts_number, blocksize),
25832097Sjkh					    p->fts_path);
25956597Smharo					}
26058601Scharnier				}
26132097Sjkh				break;
26232097Sjkh			case FTS_DC:			/* Ignore. */
26332097Sjkh				break;
26432097Sjkh			case FTS_DNR:			/* Warn, continue. */
26532097Sjkh			case FTS_ERR:
26632097Sjkh			case FTS_NS:
26732097Sjkh				warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
26832097Sjkh				rval = 1;
26932097Sjkh				break;
27032097Sjkh			default:
27178158Sroam				if (ignorep(p))
27278158Sroam					break;
27378158Sroam
27432097Sjkh				if (p->fts_statp->st_nlink > 1 && linkchk(p))
27532097Sjkh					break;
276128772Skientzle
27758601Scharnier				if (listall || p->fts_level == 0) {
27856597Smharo					if (hflag) {
27956597Smharo						(void) prthumanval(howmany(p->fts_statp->st_blocks,
28056597Smharo							blocksize));
28156597Smharo						(void) printf("\t%s\n", p->fts_path);
28256597Smharo					} else {
28356597Smharo						(void) printf("%qd\t%s\n",
28490389Speter							(long long)howmany(p->fts_statp->st_blocks, blocksize),
28556597Smharo							p->fts_path);
28656597Smharo					}
28758601Scharnier				}
28832097Sjkh
28932097Sjkh				p->fts_parent->fts_number += p->fts_statp->st_blocks;
2901590Srgrimes		}
29139076Sdes		savednumber = p->fts_parent->fts_number;
29232097Sjkh	}
29332097Sjkh
2941590Srgrimes	if (errno)
2951590Srgrimes		err(1, "fts_read");
29632097Sjkh
29758601Scharnier	if (cflag) {
29856597Smharo		if (hflag) {
29956597Smharo			(void) prthumanval(howmany(savednumber, blocksize));
30056597Smharo			(void) printf("\ttotal\n");
30156597Smharo		} else {
30256597Smharo			(void) printf("%ld\ttotal\n", howmany(savednumber, blocksize));
30356597Smharo		}
30458601Scharnier	}
30532097Sjkh
30678158Sroam	ignoreclean();
30728891Swosch	exit(rval);
3081590Srgrimes}
3091590Srgrimes
310128772Skientzlestatic int
311128772Skientzlelinkchk(FTSENT *p)
312128772Skientzle{
313128772Skientzle	struct links_entry {
314128806Skientzle		struct links_entry *next;
315128806Skientzle		struct links_entry *previous;
316128806Skientzle		int	 links;
317128806Skientzle		dev_t	 dev;
318128806Skientzle		ino_t	 ino;
319128772Skientzle	};
320128806Skientzle	static const size_t links_hash_initial_size = 8192;
321128806Skientzle	static struct links_entry **buckets;
322128806Skientzle	static struct links_entry *free_list;
323128806Skientzle	static size_t number_buckets;
324128806Skientzle	static unsigned long number_entries;
325128806Skientzle	static char stop_allocating;
326128806Skientzle	struct links_entry *le, **new_buckets;
327128806Skientzle	struct stat *st;
328128806Skientzle	size_t i, new_size;
329128834Skientzle	int count, hash;
33032097Sjkh
331128772Skientzle	st = p->fts_statp;
33232097Sjkh
333128772Skientzle	/* If necessary, initialize the hash table. */
334128772Skientzle	if (buckets == NULL) {
335128772Skientzle		number_buckets = links_hash_initial_size;
336128772Skientzle		buckets = malloc(number_buckets * sizeof(buckets[0]));
337128772Skientzle		if (buckets == NULL)
338128834Skientzle			errx(1, "No memory for hardlink detection");
339128772Skientzle		for (i = 0; i < number_buckets; i++)
340128772Skientzle			buckets[i] = NULL;
341128772Skientzle	}
3421590Srgrimes
343128772Skientzle	/* If the hash table is getting too full, enlarge it. */
344128772Skientzle	if (number_entries > number_buckets * 10 && !stop_allocating) {
345128772Skientzle		new_size = number_buckets * 2;
346128772Skientzle		new_buckets = malloc(new_size * sizeof(struct links_entry *));
347128772Skientzle		count = 0;
348128772Skientzle
349128772Skientzle		/* Try releasing the free list to see if that helps. */
350128772Skientzle		if (new_buckets == NULL && free_list != NULL) {
351128772Skientzle			while (free_list != NULL) {
352128772Skientzle				le = free_list;
353128772Skientzle				free_list = le->next;
354128772Skientzle				free(le);
355128772Skientzle			}
356128772Skientzle			new_buckets = malloc(new_size * sizeof(new_buckets[0]));
357128772Skientzle		}
358128772Skientzle
359128772Skientzle		if (new_buckets == NULL) {
360128772Skientzle			stop_allocating = 1;
361128834Skientzle			warnx("No more memory for tracking hard links");
362128772Skientzle		} else {
363128772Skientzle			memset(new_buckets, 0,
364128772Skientzle			    new_size * sizeof(struct links_entry *));
365128772Skientzle			for (i = 0; i < number_buckets; i++) {
366128772Skientzle				while (buckets[i] != NULL) {
367128772Skientzle					/* Remove entry from old bucket. */
368128772Skientzle					le = buckets[i];
369128772Skientzle					buckets[i] = le->next;
370128772Skientzle
371128772Skientzle					/* Add entry to new bucket. */
372128772Skientzle					hash = (le->dev ^ le->ino) % new_size;
373128772Skientzle
374128772Skientzle					if (new_buckets[hash] != NULL)
375128772Skientzle						new_buckets[hash]->previous =
376128772Skientzle						    le;
377128772Skientzle					le->next = new_buckets[hash];
378128772Skientzle					le->previous = NULL;
379128772Skientzle					new_buckets[hash] = le;
380128772Skientzle				}
381128772Skientzle			}
382128772Skientzle			free(buckets);
383128772Skientzle			buckets = new_buckets;
384128772Skientzle			number_buckets = new_size;
385128772Skientzle		}
386128772Skientzle	}
387128772Skientzle
388128772Skientzle	/* Try to locate this entry in the hash table. */
389128772Skientzle	hash = ( st->st_dev ^ st->st_ino ) % number_buckets;
390128772Skientzle	for (le = buckets[hash]; le != NULL; le = le->next) {
391128772Skientzle		if (le->dev == st->st_dev && le->ino == st->st_ino) {
392128772Skientzle			/*
393128772Skientzle			 * Save memory by releasing an entry when we've seen
394128772Skientzle			 * all of it's links.
395128772Skientzle			 */
396128772Skientzle			if (--le->links <= 0) {
397128772Skientzle				if (le->previous != NULL)
398128772Skientzle					le->previous->next = le->next;
399128772Skientzle				if (le->next != NULL)
400128772Skientzle					le->next->previous = le->previous;
401128772Skientzle				if (buckets[hash] == le)
402128772Skientzle					buckets[hash] = le->next;
403128772Skientzle				number_entries--;
404128772Skientzle				/* Recycle this node through the free list */
405128772Skientzle				if (stop_allocating) {
406128772Skientzle					free(le);
407128772Skientzle				} else {
408128772Skientzle					le->next = free_list;
409128772Skientzle					free_list = le;
410128772Skientzle				}
411128772Skientzle			}
412128772Skientzle			return (1);
413128772Skientzle		}
414128772Skientzle	}
415128772Skientzle
416128772Skientzle	if (stop_allocating)
417128772Skientzle		return (0);
418128772Skientzle
419128772Skientzle	/* Add this entry to the links cache. */
420128772Skientzle	if (free_list != NULL) {
421128772Skientzle		/* Pull a node from the free list if we can. */
422128772Skientzle		le = free_list;
423128772Skientzle		free_list = le->next;
424128772Skientzle	} else
425128772Skientzle		/* Malloc one if we have to. */
426128772Skientzle		le = malloc(sizeof(struct links_entry));
427128772Skientzle	if (le == NULL) {
428128772Skientzle		stop_allocating = 1;
429128834Skientzle		warnx("No more memory for tracking hard links");
430128772Skientzle		return (0);
431128772Skientzle	}
432128772Skientzle	le->dev = st->st_dev;
433128772Skientzle	le->ino = st->st_ino;
434128772Skientzle	le->links = st->st_nlink - 1;
435128772Skientzle	number_entries++;
436128772Skientzle	le->next = buckets[hash];
437128772Skientzle	le->previous = NULL;
438128772Skientzle	if (buckets[hash] != NULL)
439128772Skientzle		buckets[hash]->previous = le;
440128772Skientzle	buckets[hash] = le;
4411590Srgrimes	return (0);
4421590Srgrimes}
4431590Srgrimes
44456597Smharo/*
44556597Smharo * Output in "human-readable" format.  Uses 3 digits max and puts
44656597Smharo * unit suffixes at the end.  Makes output compact and easy to read,
44756597Smharo * especially on huge disks.
44856597Smharo *
44956597Smharo */
45056597Smharounit_t
451100822Sdwmaloneunit_adjust(double *val)
45256597Smharo{
45356597Smharo	double abval;
45456597Smharo	unit_t unit;
45556597Smharo	unsigned int unit_sz;
45656597Smharo
45756597Smharo	abval = fabs(*val);
45856597Smharo
45956597Smharo	unit_sz = abval ? ilogb(abval) / 10 : 0;
46056597Smharo
46156597Smharo	if (unit_sz >= UNIT_MAX) {
46256597Smharo		unit = NONE;
46356597Smharo	} else {
46456597Smharo		unit = unitp[unit_sz];
46556597Smharo		*val /= (double)valp[unit_sz];
46656597Smharo	}
46756597Smharo
46856597Smharo	return (unit);
46956597Smharo}
47056597Smharo
47156597Smharovoid
472100822Sdwmaloneprthumanval(double bytes)
47356597Smharo{
47456597Smharo	unit_t unit;
47556597Smharo
47656597Smharo	bytes *= 512;
47756597Smharo	unit = unit_adjust(&bytes);
47856597Smharo
47956597Smharo	if (bytes == 0)
48056597Smharo		(void)printf("  0B");
48156597Smharo	else if (bytes > 10)
48256597Smharo		(void)printf("%3.0f%c", bytes, "BKMGTPE"[unit]);
48356597Smharo	else
48456597Smharo		(void)printf("%3.1f%c", bytes, "BKMGTPE"[unit]);
48556597Smharo}
48656597Smharo
48727099Scharnierstatic void
488100822Sdwmaloneusage(void)
4891590Srgrimes{
4901590Srgrimes	(void)fprintf(stderr,
49178158Sroam		"usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k] [-x] [-I mask] [file ...]\n");
49256597Smharo	exit(EX_USAGE);
4931590Srgrimes}
49478158Sroam
49578158Sroamvoid
496100822Sdwmaloneignoreadd(const char *mask)
49778158Sroam{
49878158Sroam	struct ignentry *ign;
49978158Sroam
50078158Sroam	ign = calloc(1, sizeof(*ign));
50178158Sroam	if (ign == NULL)
50278158Sroam		errx(1, "cannot allocate memory");
50378158Sroam	ign->mask = strdup(mask);
50478158Sroam	if (ign->mask == NULL)
50578158Sroam		errx(1, "cannot allocate memory");
50678158Sroam	SLIST_INSERT_HEAD(&ignores, ign, next);
50778158Sroam}
50878158Sroam
50978158Sroamvoid
510100822Sdwmaloneignoreclean(void)
51178158Sroam{
51278158Sroam	struct ignentry *ign;
51378158Sroam
51478158Sroam	while (!SLIST_EMPTY(&ignores)) {
51578158Sroam		ign = SLIST_FIRST(&ignores);
51678158Sroam		SLIST_REMOVE_HEAD(&ignores, next);
51778158Sroam		free(ign->mask);
51878158Sroam		free(ign);
51978158Sroam	}
52078158Sroam}
52178158Sroam
52278158Sroamint
523100822Sdwmaloneignorep(FTSENT *ent)
52478158Sroam{
52578158Sroam	struct ignentry *ign;
52678158Sroam
52778158Sroam	SLIST_FOREACH(ign, &ignores, next)
52878158Sroam		if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH)
52978158Sroam			return 1;
53078158Sroam	return 0;
53178158Sroam}
532