quot.c revision 98542
1139749Simp/*
266550Snyan * Copyright (C) 1991, 1994 Wolfgang Solfrank.
366550Snyan * Copyright (C) 1991, 1994 TooLs GmbH.
466550Snyan * All rights reserved.
566550Snyan *
666550Snyan * Redistribution and use in source and binary forms, with or without
766550Snyan * modification, are permitted provided that the following conditions
866550Snyan * are met:
966550Snyan * 1. Redistributions of source code must retain the above copyright
1066550Snyan *    notice, this list of conditions and the following disclaimer.
1166550Snyan * 2. Redistributions in binary form must reproduce the above copyright
1266550Snyan *    notice, this list of conditions and the following disclaimer in the
1366550Snyan *    documentation and/or other materials provided with the distribution.
1466550Snyan * 3. All advertising materials mentioning features or use of this software
1566550Snyan *    must display the following acknowledgement:
1666550Snyan *	This product includes software developed by TooLs GmbH.
1766550Snyan * 4. The name of TooLs GmbH may not be used to endorse or promote products
1866550Snyan *    derived from this software without specific prior written permission.
1966550Snyan *
2066550Snyan * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2166550Snyan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2266550Snyan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2366550Snyan * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2466550Snyan * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2566550Snyan * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2666550Snyan * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2766550Snyan * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2866550Snyan * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29119419Sobrien * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30119419Sobrien */
31119419Sobrien
3266550Snyan#ifndef lint
3366550Snyanstatic const char rcsid[] =
3466550Snyan  "$FreeBSD: head/usr.sbin/quot/quot.c 98542 2002-06-21 06:18:05Z mckusick $";
3566550Snyan#endif /* not lint */
3666550Snyan
3766550Snyan#include <sys/param.h>
3866550Snyan#include <sys/stdint.h>
3966550Snyan#include <sys/mount.h>
4066550Snyan#include <sys/disklabel.h>
4166550Snyan#include <sys/time.h>
4266550Snyan#include <ufs/ufs/dinode.h>
4366550Snyan#include <ufs/ffs/fs.h>
4466550Snyan
4566550Snyan#include <err.h>
4666550Snyan#include <fcntl.h>
4766550Snyan#include <fstab.h>
4866550Snyan#include <errno.h>
4966550Snyan#include <paths.h>
5066550Snyan#include <pwd.h>
5166550Snyan#include <stdio.h>
5266550Snyan#include <stdlib.h>
5366550Snyan#include <string.h>
5466550Snyan#include <unistd.h>
5566550Snyan
5666550Snyan/* some flags of what to do: */
5766550Snyanstatic char estimate;
5866550Snyanstatic char count;
5966550Snyanstatic char unused;
6066550Snyanstatic void (*func)(int, struct fs *, char *);
6166550Snyanstatic long blocksize;
6266550Snyanstatic char *header;
6366550Snyanstatic int headerlen;
6466550Snyan
6566550Snyanstatic union dinode *get_inode(int, struct fs *, ino_t);
6666550Snyanstatic int	virtualblocks(struct fs *, union dinode *);
6766550Snyanstatic int	isfree(struct fs *, union dinode *);
6866550Snyanstatic void	inituser(void);
69242871Snyanstatic void	usrrehash(void);
7066550Snyanstatic struct user *user(uid_t);
7166550Snyanstatic int	cmpusers(const void *, const void *);
7266550Snyanstatic void	uses(uid_t, daddr_t, time_t);
7366550Snyanstatic void	initfsizes(void);
7466550Snyanstatic void	dofsizes(int, struct fs *, char *);
7566550Snyanstatic void	douser(int, struct fs *, char *);
7666550Snyanstatic void	donames(int, struct fs *, char *);
7766550Snyanstatic void	usage(void);
7866550Snyanstatic void	quot(char *, char *);
7966550Snyan
8066550Snyan/*
8166550Snyan * Original BSD quot doesn't round to number of frags/blocks,
8266550Snyan * doesn't account for indirection blocks and gets it totally
8366581Snyan * wrong if the	size is a multiple of the blocksize.
8466550Snyan * The new code always counts the number of 512 byte blocks
8566550Snyan * instead of the number of kilobytes and converts them	to
8666550Snyan * kByte when done (on request).
8766550Snyan *
8866550Snyan * Due to the size of modern disks, we must cast intermediate
8966550Snyan * values to 64 bits to prevent potential overflows.
9066550Snyan */
9166550Snyan#ifdef	COMPAT
92242871Snyan#define	SIZE(n)	(n)
9366550Snyan#else
9466550Snyan#define	SIZE(n) ((int)(((quad_t)(n) * 512 + blocksize - 1)/blocksize))
9566550Snyan#endif
9666550Snyan
9766550Snyan#define	INOCNT(fs)	((fs)->fs_ipg)
9866550Snyan#define	INOSZ(fs) \
9966550Snyan	(((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \
10066550Snyan	sizeof(struct ufs2_dinode)) * INOCNT(fs))
10166550Snyan
10266550Snyanunion dinode {
10366550Snyan	struct ufs1_dinode dp1;
10466550Snyan	struct ufs2_dinode dp2;
10566550Snyan};
10666581Snyan#define	DIP(fs, dp, field) \
10766550Snyan	(((fs)->fs_magic == FS_UFS1_MAGIC) ? \
10866550Snyan	(dp)->dp1.field : (dp)->dp2.field)
10966550Snyan
11066550Snyanstatic union dinode *
11166550Snyanget_inode(fd,super,ino)
11266550Snyan	int fd;
11366550Snyan	struct fs *super;
11466550Snyan	ino_t ino;
115242871Snyan{
11666550Snyan	static caddr_t ipbuf;
11766550Snyan	static ino_t last;
11866550Snyan
11966550Snyan	if (fd < 0) {		/* flush cache */
120127135Snjl		if (ipbuf) {
12166550Snyan			free(ipbuf);
12266550Snyan			ipbuf = 0;
12366550Snyan		}
12466550Snyan		return 0;
12566550Snyan	}
12666581Snyan
12766550Snyan	if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
12866550Snyan		if (!ipbuf
12966550Snyan		    && !(ipbuf = malloc(INOSZ(super))))
13066550Snyan			errx(1, "allocate inodes");
13166550Snyan		last = (ino / INOCNT(super)) * INOCNT(super);
13266550Snyan		if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0
13366550Snyan		    || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super))
13466550Snyan			err(1, "read inodes");
135242871Snyan	}
13666550Snyan
13766550Snyan	if (super->fs_magic == FS_UFS1_MAGIC)
13866550Snyan		return ((union dinode *)
13966550Snyan		    &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]);
14066550Snyan	return ((union dinode *)
14166550Snyan	    &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]);
14266550Snyan}
14366550Snyan
14466550Snyan#ifdef	COMPAT
14566550Snyan#define	actualblocks(fs, dp)	(DIP(fs, dp, di_blocks) / 2)
14666550Snyan#else
14766550Snyan#define	actualblocks(fs, dp)	DIP(fs, dp, di_blocks)
14866550Snyan#endif
14966550Snyan
15066550Snyanstatic int virtualblocks(super, dp)
15166550Snyan	struct fs *super;
15266550Snyan	union dinode *dp;
15366550Snyan{
154181298Sjhb	register off_t nblk, sz;
155181298Sjhb
156181298Sjhb	sz = DIP(super, dp, di_size);
157181298Sjhb#ifdef	COMPAT
15866550Snyan	if (lblkno(super,sz) >= NDADDR) {
15966550Snyan		nblk = blkroundup(super,sz);
16066550Snyan		if (sz == nblk)
16166550Snyan			nblk += super->fs_bsize;
16266550Snyan	}
16366550Snyan
16466550Snyan	return sz / 1024;
165242871Snyan
16666550Snyan#else	/* COMPAT */
16766550Snyan
16866550Snyan	if (lblkno(super,sz) >= NDADDR) {
16966550Snyan		nblk = blkroundup(super,sz);
17066550Snyan		sz = lblkno(super,nblk);
17166550Snyan		sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super);
17266550Snyan		while (sz > 0) {
17366550Snyan			nblk += sz * super->fs_bsize;
17466550Snyan			/* sz - 1 rounded up */
17566550Snyan			sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super);
17666550Snyan		}
17766550Snyan	} else
17866550Snyan		nblk = fragroundup(super,sz);
17966550Snyan
18066550Snyan	return nblk / 512;
181242871Snyan#endif	/* COMPAT */
18266550Snyan}
18366550Snyan
18466550Snyanstatic int
185181298Sjhbisfree(super, dp)
18666550Snyan	struct fs *super;
18766550Snyan	union dinode *dp;
18866550Snyan{
18966550Snyan#ifdef	COMPAT
19066550Snyan	return (DIP(super, dp, di_mode) & IFMT) == 0;
19166550Snyan#else	/* COMPAT */
19266550Snyan
19366550Snyan	switch (DIP(super, dp, di_mode) & IFMT) {
19466550Snyan	case IFIFO:
19566550Snyan	case IFLNK:		/* should check FASTSYMLINK? */
19666550Snyan	case IFDIR:
19766550Snyan	case IFREG:
19866550Snyan		return 0;
19966550Snyan	default:
20066550Snyan		return 1;
20166550Snyan	}
20266550Snyan#endif
20366550Snyan}
20466550Snyan
20566550Snyanstatic struct user {
20666550Snyan	uid_t uid;
20766550Snyan	char *name;
20866550Snyan	daddr_t space;
20966550Snyan	long count;
21066550Snyan	daddr_t spc30;
21166550Snyan	daddr_t spc60;
21266550Snyan	daddr_t spc90;
21366550Snyan} *users;
21466550Snyanstatic int nusers;
21566550Snyan
21666550Snyanstatic void
217181298Sjhbinituser()
218181298Sjhb{
219181298Sjhb	register int i;
220181298Sjhb	register struct user *usr;
221181298Sjhb
222181298Sjhb	if (!nusers) {
223181298Sjhb		nusers = 8;
224181298Sjhb		if (!(users =
225181298Sjhb		    (struct user *)calloc(nusers,sizeof(struct user))))
22666550Snyan			errx(1, "allocate users");
227181298Sjhb	} else {
228181298Sjhb		for (usr = users, i = nusers; --i >= 0; usr++) {
229181298Sjhb			usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
230181298Sjhb			usr->count = 0;
231181298Sjhb		}
232181298Sjhb	}
233181298Sjhb}
234181298Sjhb
235181298Sjhbstatic void
236181298Sjhbusrrehash()
23766550Snyan{
23866550Snyan	register int i;
23966550Snyan	register struct user *usr, *usrn;
24066550Snyan	struct user *svusr;
24166550Snyan
24266550Snyan	svusr = users;
24366550Snyan	nusers <<= 1;
244194023Savg	if (!(users = (struct user *)calloc(nusers,sizeof(struct user))))
245242871Snyan		errx(1, "allocate users");
24666550Snyan	for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
247181298Sjhb		for (usrn = users + (usr->uid&(nusers - 1)); usrn->name;
248181298Sjhb		    usrn--) {
249181298Sjhb			if (usrn <= users)
250181298Sjhb				usrn = users + nusers;
251181298Sjhb		}
252194023Savg		*usrn = *usr;
253194023Savg	}
25466550Snyan}
255
256static struct user *
257user(uid)
258	uid_t uid;
259{
260	register struct user *usr;
261	register int i;
262	struct passwd *pwd;
263
264	while (1) {
265		for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0;
266		    usr--) {
267			if (!usr->name) {
268				usr->uid = uid;
269
270				if (!(pwd = getpwuid(uid))) {
271					if ((usr->name = (char *)malloc(7)))
272						sprintf(usr->name,"#%d",uid);
273				} else {
274					if ((usr->name = (char *)
275					    malloc(strlen(pwd->pw_name) + 1)))
276						strcpy(usr->name,pwd->pw_name);
277				}
278				if (!usr->name)
279					errx(1, "allocate users");
280
281				return usr;
282
283			} else if (usr->uid == uid)
284				return usr;
285
286			if (usr <= users)
287				usr = users + nusers;
288		}
289		usrrehash();
290	}
291}
292
293static int
294cmpusers(v1,v2)
295	const void *v1, *v2;
296{
297	const struct user *u1, *u2;
298	u1 = (const struct user *)v1;
299	u2 = (const struct user *)v2;
300
301	return u2->space - u1->space;
302}
303
304#define	sortusers(users)	(qsort((users),nusers,sizeof(struct user), \
305				    cmpusers))
306
307static void
308uses(uid,blks,act)
309	uid_t uid;
310	daddr_t blks;
311	time_t act;
312{
313	static time_t today;
314	register struct user *usr;
315
316	if (!today)
317		time(&today);
318
319	usr = user(uid);
320	usr->count++;
321	usr->space += blks;
322
323	if (today - act > 90L * 24L * 60L * 60L)
324		usr->spc90 += blks;
325	if (today - act > 60L * 24L * 60L * 60L)
326		usr->spc60 += blks;
327	if (today - act > 30L * 24L * 60L * 60L)
328		usr->spc30 += blks;
329}
330
331#ifdef	COMPAT
332#define	FSZCNT	500
333#else
334#define	FSZCNT	512
335#endif
336struct fsizes {
337	struct fsizes *fsz_next;
338	daddr_t fsz_first, fsz_last;
339	ino_t fsz_count[FSZCNT];
340	daddr_t fsz_sz[FSZCNT];
341} *fsizes;
342
343static void
344initfsizes()
345{
346	register struct fsizes *fp;
347	register int i;
348
349	for (fp = fsizes; fp; fp = fp->fsz_next) {
350		for (i = FSZCNT; --i >= 0;) {
351			fp->fsz_count[i] = 0;
352			fp->fsz_sz[i] = 0;
353		}
354	}
355}
356
357static void
358dofsizes(fd, super, name)
359	int fd;
360	struct fs *super;
361	char *name;
362{
363	ino_t inode, maxino;
364	union dinode *dp;
365	daddr_t sz, ksz;
366	struct fsizes *fp, **fsp;
367	register int i;
368
369	maxino = super->fs_ncg * super->fs_ipg - 1;
370#ifdef	COMPAT
371	if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes))))
372		errx(1, "allocate fsize structure");
373#endif	/* COMPAT */
374	for (inode = 0; inode < maxino; inode++) {
375		errno = 0;
376		if ((dp = get_inode(fd,super,inode))
377#ifdef	COMPAT
378		    && ((DIP(super, dp, di_mode) & IFMT) == IFREG
379			|| (DIP(super, dp, di_mode) & IFMT) == IFDIR)
380#else	/* COMPAT */
381		    && !isfree(super, dp)
382#endif	/* COMPAT */
383		    ) {
384			sz = estimate ? virtualblocks(super, dp) :
385			    actualblocks(super, dp);
386#ifdef	COMPAT
387			if (sz >= FSZCNT) {
388				fsizes->fsz_count[FSZCNT-1]++;
389				fsizes->fsz_sz[FSZCNT-1] += sz;
390			} else {
391				fsizes->fsz_count[sz]++;
392				fsizes->fsz_sz[sz] += sz;
393			}
394#else	/* COMPAT */
395			ksz = SIZE(sz);
396			for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) {
397				if (ksz < fp->fsz_last)
398					break;
399			}
400			if (!fp || ksz < fp->fsz_first) {
401				if (!(fp = (struct fsizes *)
402				    malloc(sizeof(struct fsizes))))
403					errx(1, "allocate fsize structure");
404				fp->fsz_next = *fsp;
405				*fsp = fp;
406				fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
407				fp->fsz_last = fp->fsz_first + FSZCNT;
408				for (i = FSZCNT; --i >= 0;) {
409					fp->fsz_count[i] = 0;
410					fp->fsz_sz[i] = 0;
411				}
412			}
413			fp->fsz_count[ksz % FSZCNT]++;
414			fp->fsz_sz[ksz % FSZCNT] += sz;
415#endif	/* COMPAT */
416		} else if (errno) {
417			err(1, "%s", name);
418		}
419	}
420	sz = 0;
421	for (fp = fsizes; fp; fp = fp->fsz_next) {
422		for (i = 0; i < FSZCNT; i++) {
423			if (fp->fsz_count[i])
424				printf("%jd\t%jd\t%d\n",
425				    (intmax_t)(fp->fsz_first + i),
426				    (intmax_t)fp->fsz_count[i],
427				    SIZE(sz += fp->fsz_sz[i]));
428		}
429	}
430}
431
432static void
433douser(fd, super, name)
434	int fd;
435	struct fs *super;
436	char *name;
437{
438	ino_t inode, maxino;
439	struct user *usr, *usrs;
440	union dinode *dp;
441	register int n;
442
443	maxino = super->fs_ncg * super->fs_ipg - 1;
444	for (inode = 0; inode < maxino; inode++) {
445		errno = 0;
446		if ((dp = get_inode(fd,super,inode))
447		    && !isfree(super, dp))
448			uses(DIP(super, dp, di_uid),
449			    estimate ? virtualblocks(super, dp) :
450				actualblocks(super, dp),
451			    DIP(super, dp, di_atime));
452		else if (errno) {
453			err(1, "%s", name);
454		}
455	}
456	if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user))))
457		errx(1, "allocate users");
458	bcopy(users,usrs,nusers * sizeof(struct user));
459	sortusers(usrs);
460	for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
461		printf("%5d",SIZE(usr->space));
462		if (count)
463			printf("\t%5ld",usr->count);
464		printf("\t%-8s",usr->name);
465		if (unused)
466			printf("\t%5d\t%5d\t%5d",
467			       SIZE(usr->spc30),
468			       SIZE(usr->spc60),
469			       SIZE(usr->spc90));
470		printf("\n");
471	}
472	free(usrs);
473}
474
475static void
476donames(fd, super, name)
477	int fd;
478	struct fs *super;
479	char *name;
480{
481	int c;
482	ino_t inode, inode1;
483	ino_t maxino;
484	union dinode *dp;
485
486	maxino = super->fs_ncg * super->fs_ipg - 1;
487	/* first skip the name of the filesystem */
488	while ((c = getchar()) != EOF && (c < '0' || c > '9'))
489		while ((c = getchar()) != EOF && c != '\n');
490	ungetc(c,stdin);
491	inode1 = -1;
492	while (scanf("%u",&inode) == 1) {
493		if (inode > maxino) {
494			warnx("illegal inode %d",inode);
495			return;
496		}
497		errno = 0;
498		if ((dp = get_inode(fd,super,inode))
499		    && !isfree(super, dp)) {
500			printf("%s\t",user(DIP(super, dp, di_uid))->name);
501			/* now skip whitespace */
502			while ((c = getchar()) == ' ' || c == '\t');
503			/* and print out the remainder of the input line */
504			while (c != EOF && c != '\n') {
505				putchar(c);
506				c = getchar();
507			}
508			putchar('\n');
509			inode1 = inode;
510		} else {
511			if (errno) {
512				err(1, "%s", name);
513			}
514			/* skip this line */
515			while ((c = getchar()) != EOF && c != '\n');
516		}
517		if (c == EOF)
518			break;
519	}
520}
521
522static void
523usage()
524{
525#ifdef	COMPAT
526	fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n");
527#else	/* COMPAT */
528	fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n");
529#endif	/* COMPAT */
530	exit(1);
531}
532
533/*
534 * Possible superblock locations ordered from most to least likely.
535 */
536static int sblock_try[] = SBLOCKSEARCH;
537static char superblock[SBLOCKSIZE];
538
539void
540quot(name,mp)
541	char *name, *mp;
542{
543	int i, fd;
544	struct fs *fs;
545
546	get_inode(-1, NULL, 0);		/* flush cache */
547	inituser();
548	initfsizes();
549	if ((fd = open(name,0)) < 0) {
550		warn("%s", name);
551		close(fd);
552		return;
553	}
554	for (i = 0; sblock_try[i] != -1; i++) {
555		if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) {
556			close(fd);
557			return;
558		}
559		if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) {
560			close(fd);
561			return;
562		}
563		fs = (struct fs *)superblock;
564		if ((fs->fs_magic == FS_UFS1_MAGIC ||
565		     (fs->fs_magic == FS_UFS2_MAGIC &&
566		      fs->fs_sblockloc == numfrags(fs, sblock_try[i]))) &&
567		    fs->fs_bsize <= MAXBSIZE &&
568		    fs->fs_bsize >= sizeof(struct fs))
569			break;
570	}
571	if (sblock_try[i] == -1) {
572		warnx("%s: not a BSD filesystem",name);
573		close(fd);
574		return;
575	}
576	printf("%s:",name);
577	if (mp)
578		printf(" (%s)",mp);
579	putchar('\n');
580	(*func)(fd, fs, name);
581	close(fd);
582}
583
584int
585main(argc,argv)
586	int argc;
587	char **argv;
588{
589	char all = 0;
590	struct statfs *mp;
591	struct fstab *fs;
592	char dev[MNAMELEN + 1];
593	char *nm;
594	int cnt;
595
596	func = douser;
597#ifndef	COMPAT
598	header = getbsize(&headerlen,&blocksize);
599#endif
600	while (--argc > 0 && **++argv == '-') {
601		while (*++*argv) {
602			switch (**argv) {
603			case 'n':
604				func = donames;
605				break;
606			case 'c':
607				func = dofsizes;
608				break;
609			case 'a':
610				all = 1;
611				break;
612			case 'f':
613				count = 1;
614				break;
615			case 'h':
616				estimate = 1;
617				break;
618#ifndef	COMPAT
619			case 'k':
620				blocksize = 1024;
621				break;
622#endif	/* COMPAT */
623			case 'v':
624				unused = 1;
625				break;
626			default:
627				usage();
628			}
629		}
630	}
631	if (all) {
632		cnt = getmntinfo(&mp,MNT_NOWAIT);
633		for (; --cnt >= 0; mp++) {
634			if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) {
635				if ((nm = strrchr(mp->f_mntfromname,'/'))) {
636					sprintf(dev,"%s%s",_PATH_DEV,nm + 1);
637					nm = dev;
638				} else
639					nm = mp->f_mntfromname;
640				quot(nm,mp->f_mntonname);
641			}
642		}
643	}
644	while (--argc >= 0) {
645		if ((fs = getfsfile(*argv)) != NULL)
646			quot(fs->fs_spec, 0);
647		else
648			quot(*argv,0);
649		argv++;
650	}
651	return 0;
652}
653