1/*
2 * Copyright (c) 1989, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Chris Newcomb.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38static const char copyright[] =
39"@(#) Copyright (c) 1989, 1993, 1994\n\
40	The Regents of the University of California.  All rights reserved.\n";
41#endif /* not lint */
42
43#ifndef lint
44#if 0
45static const char sccsid[] = "@(#)du.c	8.5 (Berkeley) 5/4/95";
46#endif
47#endif /* not lint */
48#include <sys/cdefs.h>
49__FBSDID("$FreeBSD: src/usr.bin/du/du.c,v 1.38 2005/04/09 14:31:40 stefanf Exp $");
50
51#include <sys/mount.h>
52#include <sys/param.h>
53#include <sys/queue.h>
54#include <sys/stat.h>
55#include <sys/attr.h>
56
57#include <err.h>
58#include <errno.h>
59#include <fnmatch.h>
60#include <fts.h>
61#include <locale.h>
62#include <math.h>
63#include <stdint.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67#include <sysexits.h>
68#include <unistd.h>
69
70#ifdef __APPLE__
71#include <get_compat.h>
72#else
73#define COMPAT_MODE(func, mode) (1)
74#endif
75
76#define	KILO_SZ(n) (n)
77#define	MEGA_SZ(n) ((n) * (n))
78#define	GIGA_SZ(n) ((n) * (n) * (n))
79#define	TERA_SZ(n) ((n) * (n) * (n) * (n))
80#define	PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
81
82#define	KILO_2_SZ (KILO_SZ(1024ULL))
83#define	MEGA_2_SZ (MEGA_SZ(1024ULL))
84#define	GIGA_2_SZ (GIGA_SZ(1024ULL))
85#define	TERA_2_SZ (TERA_SZ(1024ULL))
86#define	PETA_2_SZ (PETA_SZ(1024ULL))
87
88#define	KILO_SI_SZ (KILO_SZ(1000ULL))
89#define	MEGA_SI_SZ (MEGA_SZ(1000ULL))
90#define	GIGA_SI_SZ (GIGA_SZ(1000ULL))
91#define	TERA_SI_SZ (TERA_SZ(1000ULL))
92#define	PETA_SI_SZ (PETA_SZ(1000ULL))
93
94#define TWO_TB  (2LL * 1024LL * 1024LL * 1024LL * 1024LL)
95
96unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
97unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
98unsigned long long *valp;
99
100typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t;
101
102int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA };
103
104SLIST_HEAD(ignhead, ignentry) ignores;
105struct ignentry {
106	char			*mask;
107	SLIST_ENTRY(ignentry)	next;
108};
109
110static int	linkchk(FTSENT *);
111static int	dirlinkchk(FTSENT *);
112static void	usage(void);
113void		prthumanval(double);
114unit_t		unit_adjust(double *);
115void		ignoreadd(const char *);
116void		ignoreclean(void);
117int		ignorep(FTSENT *);
118
119int
120main(int argc, char *argv[])
121{
122	FTS		*fts;
123	FTSENT		*p;
124	off_t		savednumber = 0;
125	long		blocksize;
126	int		ftsoptions;
127	int		listall;
128	int		depth;
129	int		Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval;
130	char 		**save;
131	static char	dot[] = ".";
132	off_t           *ftsnum, *ftsparnum;
133
134	setlocale(LC_ALL, "");
135
136	Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0;
137
138	save = argv;
139	ftsoptions = FTS_NOCHDIR;
140	depth = INT_MAX;
141	SLIST_INIT(&ignores);
142
143	while ((ch = getopt(argc, argv, "HI:LPasd:cghkmrx")) != -1)
144		switch (ch) {
145			case 'H':
146				Lflag = Pflag = 0;
147				Hflag = 1;
148				break;
149			case 'I':
150				ignoreadd(optarg);
151				break;
152			case 'L':
153				Hflag = Pflag = 0;
154				Lflag = 1;
155				break;
156			case 'P':
157				Hflag = Lflag = 0;
158				Pflag = 1;
159				break;
160			case 'a':
161				aflag = 1;
162				break;
163			case 's':
164				sflag = 1;
165				break;
166			case 'd':
167				dflag = 1;
168				errno = 0;
169				depth = atoi(optarg);
170				if (errno == ERANGE || depth < 0) {
171					warnx("invalid argument to option d: %s", optarg);
172					usage();
173				}
174				break;
175			case 'c':
176				cflag = 1;
177				break;
178			case 'h':
179				putenv("BLOCKSIZE=512");
180				hflag = 1;
181				valp = vals_base2;
182				break;
183			case 'k':
184				hflag = 0;
185				putenv("BLOCKSIZE=1024");
186				break;
187			case 'm':
188				hflag = 0;
189				putenv("BLOCKSIZE=1048576");
190				break;
191			case 'g':
192				hflag = 0;
193				putenv("BLOCKSIZE=1g");
194				break;
195			case 'r':		 /* Compatibility. */
196				break;
197			case 'x':
198				ftsoptions |= FTS_XDEV;
199				break;
200			case '?':
201			default:
202				usage();
203		}
204
205//	argc -= optind;
206	argv += optind;
207
208	/*
209	 * XXX
210	 * Because of the way that fts(3) works, logical walks will not count
211	 * the blocks actually used by symbolic links.  We rationalize this by
212	 * noting that users computing logical sizes are likely to do logical
213	 * copies, so not counting the links is correct.  The real reason is
214	 * that we'd have to re-implement the kernel's symbolic link traversing
215	 * algorithm to get this right.  If, for example, you have relative
216	 * symbolic links referencing other relative symbolic links, it gets
217	 * very nasty, very fast.  The bottom line is that it's documented in
218	 * the man page, so it's a feature.
219	 */
220
221	if (Hflag + Lflag + Pflag > 1)
222		usage();
223
224	if (Hflag + Lflag + Pflag == 0)
225		Pflag = 1;			/* -P (physical) is default */
226
227	if (Hflag)
228		ftsoptions |= FTS_COMFOLLOW;
229
230	if (Lflag)
231		ftsoptions |= FTS_LOGICAL;
232
233	if (Pflag)
234		ftsoptions |= FTS_PHYSICAL;
235
236	listall = 0;
237
238	if (aflag) {
239		if (sflag || dflag)
240			usage();
241		listall = 1;
242	} else if (sflag) {
243		if (dflag)
244			usage();
245		depth = 0;
246	}
247
248	if (!*argv) {
249		argv = save;
250		argv[0] = dot;
251		argv[1] = NULL;
252	}
253
254	(void) getbsize(&notused, &blocksize);
255	blocksize /= 512;
256
257	rval = 0;
258
259	if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
260		err(1, "fts_open");
261
262	while ((p = fts_read(fts)) != NULL) {
263		switch (p->fts_info) {
264			case FTS_D:
265				if (ignorep(p) || dirlinkchk(p))
266					fts_set(fts, p, FTS_SKIP);
267				break;
268			case FTS_DP:
269				if (ignorep(p))
270					break;
271
272				ftsparnum = (off_t *)&p->fts_parent->fts_number;
273				ftsnum = (off_t *)&p->fts_number;
274				if (p->fts_statp->st_size < TWO_TB) {
275				    ftsparnum[0] += ftsnum[0] += p->fts_statp->st_blocks;
276				} else {
277				    ftsparnum[0] += ftsnum[0] += howmany(p->fts_statp->st_size, 512LL);
278				}
279
280				if (p->fts_level <= depth) {
281					if (hflag) {
282						(void) prthumanval(howmany(*ftsnum, blocksize));
283						(void) printf("\t%s\n", p->fts_path);
284					} else {
285					(void) printf("%jd\t%s\n",
286					    (intmax_t)howmany(*ftsnum, blocksize),
287					    p->fts_path);
288					}
289				}
290				break;
291			case FTS_DC:			/* Ignore. */
292				if (COMPAT_MODE("bin/du", "unix2003")) {
293					errx(1, "Can't follow symlink cycle from %s to %s", p->fts_path, p->fts_cycle->fts_path);
294				}
295				break;
296			case FTS_DNR:			/* Warn, continue. */
297			case FTS_ERR:
298			case FTS_NS:
299				warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
300				rval = 1;
301				break;
302			case FTS_SLNONE:
303				if (COMPAT_MODE("bin/du", "unix2003")) {
304					struct stat sb;
305					int rc = stat(p->fts_path, &sb);
306					if (rc < 0 && errno == ELOOP) {
307						errx(1, "Too many symlinks at %s", p->fts_path);
308					}
309				}
310			default:
311				if (ignorep(p))
312					break;
313
314				if (p->fts_statp->st_nlink > 1 && linkchk(p))
315					break;
316
317				if (listall || p->fts_level == 0) {
318					if (hflag) {
319					    if (p->fts_statp->st_size < TWO_TB) {
320						(void) prthumanval(howmany(p->fts_statp->st_blocks,
321							blocksize));
322					    } else {
323						(void) prthumanval(howmany(howmany(p->fts_statp->st_size, 512LL),
324							blocksize));
325					    }
326						(void) printf("\t%s\n", p->fts_path);
327					} else {
328					    if (p->fts_statp->st_size < TWO_TB) {
329						(void) printf("%jd\t%s\n",
330							(intmax_t)howmany(p->fts_statp->st_blocks, blocksize),
331							p->fts_path);
332					    } else {
333						(void) printf("%jd\t%s\n",
334							(intmax_t)howmany(howmany(p->fts_statp->st_size, 512LL), blocksize),
335							p->fts_path);
336					    }
337					}
338				}
339
340				ftsparnum = (off_t *)&p->fts_parent->fts_number;
341				if (p->fts_statp->st_size < TWO_TB) {
342				    ftsparnum[0] += p->fts_statp->st_blocks;
343				} else {
344				    ftsparnum[0] += p->fts_statp->st_size / 512LL;
345				}
346		}
347		savednumber = ((off_t *)&p->fts_parent->fts_number)[0];
348	}
349
350	if (errno)
351		err(1, "fts_read");
352
353	if (cflag) {
354		if (hflag) {
355			(void) prthumanval(howmany(savednumber, blocksize));
356			(void) printf("\ttotal\n");
357		} else {
358			(void) printf("%jd\ttotal\n", (intmax_t)howmany(savednumber, blocksize));
359		}
360	}
361
362	ignoreclean();
363	exit(rval);
364}
365
366static int
367linkchk(FTSENT *p)
368{
369	struct links_entry {
370		struct links_entry *next;
371		struct links_entry *previous;
372		int	 links;
373		dev_t	 dev;
374		ino_t	 ino;
375	};
376	static const size_t links_hash_initial_size = 8192;
377	static struct links_entry **buckets;
378	static struct links_entry *free_list;
379	static size_t number_buckets;
380	static unsigned long number_entries;
381	static char stop_allocating;
382	struct links_entry *le, **new_buckets;
383	struct stat *st;
384	size_t i, new_size;
385	int hash;
386
387	st = p->fts_statp;
388
389	/* If necessary, initialize the hash table. */
390	if (buckets == NULL) {
391		number_buckets = links_hash_initial_size;
392		buckets = malloc(number_buckets * sizeof(buckets[0]));
393		if (buckets == NULL)
394			errx(1, "No memory for hardlink detection");
395		for (i = 0; i < number_buckets; i++)
396			buckets[i] = NULL;
397	}
398
399	/* If the hash table is getting too full, enlarge it. */
400	if (number_entries > number_buckets * 10 && !stop_allocating) {
401		new_size = number_buckets * 2;
402		new_buckets = malloc(new_size * sizeof(struct links_entry *));
403
404		/* Try releasing the free list to see if that helps. */
405		if (new_buckets == NULL && free_list != NULL) {
406			while (free_list != NULL) {
407				le = free_list;
408				free_list = le->next;
409				free(le);
410			}
411			new_buckets = malloc(new_size * sizeof(new_buckets[0]));
412		}
413
414		if (new_buckets == NULL) {
415			stop_allocating = 1;
416			warnx("No more memory for tracking hard links");
417		} else {
418			memset(new_buckets, 0,
419			    new_size * sizeof(struct links_entry *));
420			for (i = 0; i < number_buckets; i++) {
421				while (buckets[i] != NULL) {
422					/* Remove entry from old bucket. */
423					le = buckets[i];
424					buckets[i] = le->next;
425
426					/* Add entry to new bucket. */
427					hash = (le->dev ^ le->ino) % new_size;
428
429					if (new_buckets[hash] != NULL)
430						new_buckets[hash]->previous =
431						    le;
432					le->next = new_buckets[hash];
433					le->previous = NULL;
434					new_buckets[hash] = le;
435				}
436			}
437			free(buckets);
438			buckets = new_buckets;
439			number_buckets = new_size;
440		}
441	}
442
443	/* Try to locate this entry in the hash table. */
444	hash = ( st->st_dev ^ st->st_ino ) % number_buckets;
445	for (le = buckets[hash]; le != NULL; le = le->next) {
446		if (le->dev == st->st_dev && le->ino == st->st_ino) {
447			/*
448			 * Save memory by releasing an entry when we've seen
449			 * all of it's links.
450			 */
451			if (--le->links <= 0) {
452				if (le->previous != NULL)
453					le->previous->next = le->next;
454				if (le->next != NULL)
455					le->next->previous = le->previous;
456				if (buckets[hash] == le)
457					buckets[hash] = le->next;
458				number_entries--;
459				/* Recycle this node through the free list */
460				if (stop_allocating) {
461					free(le);
462				} else {
463					le->next = free_list;
464					free_list = le;
465				}
466			}
467			return (1);
468		}
469	}
470
471	if (stop_allocating)
472		return (0);
473
474	/* Add this entry to the links cache. */
475	if (free_list != NULL) {
476		/* Pull a node from the free list if we can. */
477		le = free_list;
478		free_list = le->next;
479	} else
480		/* Malloc one if we have to. */
481		le = malloc(sizeof(struct links_entry));
482	if (le == NULL) {
483		stop_allocating = 1;
484		warnx("No more memory for tracking hard links");
485		return (0);
486	}
487	le->dev = st->st_dev;
488	le->ino = st->st_ino;
489	le->links = st->st_nlink - 1;
490	number_entries++;
491	le->next = buckets[hash];
492	le->previous = NULL;
493	if (buckets[hash] != NULL)
494		buckets[hash]->previous = le;
495	buckets[hash] = le;
496	return (0);
497}
498
499static int
500dirlinkchk(FTSENT *p)
501{
502	struct links_entry {
503		struct links_entry *next;
504		struct links_entry *previous;
505		int	 links;
506		dev_t	 dev;
507		ino_t	 ino;
508	};
509	static const size_t links_hash_initial_size = 8192;
510	static struct links_entry **buckets;
511	static struct links_entry *free_list;
512	static size_t number_buckets;
513	static unsigned long number_entries;
514	static char stop_allocating;
515	struct links_entry *le, **new_buckets;
516	struct stat *st;
517	size_t i, new_size;
518	int hash;
519	struct attrbuf {
520		int size;
521		int linkcount;
522	} buf;
523	struct attrlist attrList;
524
525	memset(&attrList, 0, sizeof(attrList));
526	attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
527	attrList.dirattr = ATTR_DIR_LINKCOUNT;
528	if (-1 == getattrlist(p->fts_path, &attrList, &buf, sizeof(buf), 0))
529		return 0;
530	if (buf.linkcount == 1)
531		return 0;
532	st = p->fts_statp;
533
534	/* If necessary, initialize the hash table. */
535	if (buckets == NULL) {
536		number_buckets = links_hash_initial_size;
537		buckets = malloc(number_buckets * sizeof(buckets[0]));
538		if (buckets == NULL)
539			errx(1, "No memory for directory hardlink detection");
540		for (i = 0; i < number_buckets; i++)
541			buckets[i] = NULL;
542	}
543
544	/* If the hash table is getting too full, enlarge it. */
545	if (number_entries > number_buckets * 10 && !stop_allocating) {
546		new_size = number_buckets * 2;
547		new_buckets = malloc(new_size * sizeof(struct links_entry *));
548
549		/* Try releasing the free list to see if that helps. */
550		if (new_buckets == NULL && free_list != NULL) {
551			while (free_list != NULL) {
552				le = free_list;
553				free_list = le->next;
554				free(le);
555			}
556			new_buckets = malloc(new_size * sizeof(new_buckets[0]));
557		}
558
559		if (new_buckets == NULL) {
560			stop_allocating = 1;
561			warnx("No more memory for tracking directory hard links");
562		} else {
563			memset(new_buckets, 0,
564			    new_size * sizeof(struct links_entry *));
565			for (i = 0; i < number_buckets; i++) {
566				while (buckets[i] != NULL) {
567					/* Remove entry from old bucket. */
568					le = buckets[i];
569					buckets[i] = le->next;
570
571					/* Add entry to new bucket. */
572					hash = (le->dev ^ le->ino) % new_size;
573
574					if (new_buckets[hash] != NULL)
575						new_buckets[hash]->previous =
576						    le;
577					le->next = new_buckets[hash];
578					le->previous = NULL;
579					new_buckets[hash] = le;
580				}
581			}
582			free(buckets);
583			buckets = new_buckets;
584			number_buckets = new_size;
585		}
586	}
587
588	/* Try to locate this entry in the hash table. */
589	hash = ( st->st_dev ^ st->st_ino ) % number_buckets;
590	for (le = buckets[hash]; le != NULL; le = le->next) {
591		if (le->dev == st->st_dev && le->ino == st->st_ino) {
592			/*
593			 * Save memory by releasing an entry when we've seen
594			 * all of it's links.
595			 */
596			if (--le->links <= 0) {
597				if (le->previous != NULL)
598					le->previous->next = le->next;
599				if (le->next != NULL)
600					le->next->previous = le->previous;
601				if (buckets[hash] == le)
602					buckets[hash] = le->next;
603				number_entries--;
604				/* Recycle this node through the free list */
605				if (stop_allocating) {
606					free(le);
607				} else {
608					le->next = free_list;
609					free_list = le;
610				}
611			}
612			return (1);
613		}
614	}
615
616	if (stop_allocating)
617		return (0);
618	/* Add this entry to the links cache. */
619	if (free_list != NULL) {
620		/* Pull a node from the free list if we can. */
621		le = free_list;
622		free_list = le->next;
623	} else
624		/* Malloc one if we have to. */
625		le = malloc(sizeof(struct links_entry));
626	if (le == NULL) {
627		stop_allocating = 1;
628		warnx("No more memory for tracking hard links");
629		return (0);
630	}
631	le->dev = st->st_dev;
632	le->ino = st->st_ino;
633	le->links = buf.linkcount - 1;
634	number_entries++;
635	le->next = buckets[hash];
636	le->previous = NULL;
637	if (buckets[hash] != NULL)
638		buckets[hash]->previous = le;
639	buckets[hash] = le;
640	return (0);
641}
642
643/*
644 * Output in "human-readable" format.  Uses 3 digits max and puts
645 * unit suffixes at the end.  Makes output compact and easy to read,
646 * especially on huge disks.
647 *
648 */
649unit_t
650unit_adjust(double *val)
651{
652	double abval;
653	unit_t unit;
654	unsigned int unit_sz;
655
656	abval = fabs(*val);
657
658	unit_sz = abval ? ilogb(abval) / 10 : 0;
659
660	if (unit_sz >= UNIT_MAX) {
661		unit = NONE;
662	} else {
663		unit = unitp[unit_sz];
664		*val /= (double)valp[unit_sz];
665	}
666
667	return (unit);
668}
669
670void
671prthumanval(double bytes)
672{
673	unit_t unit;
674
675	bytes *= 512;
676	unit = unit_adjust(&bytes);
677
678	if (bytes == 0)
679		(void)printf("  0B");
680	else if (bytes > 10)
681		(void)printf("%3.0f%c", bytes, "BKMGTPE"[unit]);
682	else
683		(void)printf("%3.1f%c", bytes, "BKMGTPE"[unit]);
684}
685
686static void
687usage(void)
688{
689	(void)fprintf(stderr,
690		"usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k | -m | -g] [-x] [-I mask] [file ...]\n");
691	exit(EX_USAGE);
692}
693
694void
695ignoreadd(const char *mask)
696{
697	struct ignentry *ign;
698
699	ign = calloc(1, sizeof(*ign));
700	if (ign == NULL)
701		errx(1, "cannot allocate memory");
702	ign->mask = strdup(mask);
703	if (ign->mask == NULL)
704		errx(1, "cannot allocate memory");
705	SLIST_INSERT_HEAD(&ignores, ign, next);
706}
707
708void
709ignoreclean(void)
710{
711	struct ignentry *ign;
712
713	while (!SLIST_EMPTY(&ignores)) {
714		ign = SLIST_FIRST(&ignores);
715		SLIST_REMOVE_HEAD(&ignores, next);
716		free(ign->mask);
717		free(ign);
718	}
719}
720
721int
722ignorep(FTSENT *ent)
723{
724	struct ignentry *ign;
725
726#ifdef __APPLE__
727	if (S_ISDIR(ent->fts_statp->st_mode) && !strcmp("fd", ent->fts_name)) {
728		struct statfs sfsb;
729		int rc = statfs(ent->fts_accpath, &sfsb);
730		if (rc >= 0 && !strcmp("devfs", sfsb.f_fstypename)) {
731			/* Don't cd into /dev/fd/N since one of those is likely to be
732			  the cwd as of the start of du which causes all manner of
733			  unpleasant surprises */
734			return 1;
735		}
736	}
737#endif /* __APPLE__ */
738	SLIST_FOREACH(ign, &ignores, next)
739		if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH)
740			return 1;
741	return 0;
742}
743