[comp.os.minix] du

nick@nswitgould.OZ (Nick Andrew) (04/02/89)

DU 1.4a BUG:

	Minix 1.4a du.c has a bug where it may incorrectly report the space
occupied by a directory if that directory contains filenames of length 14.
This bug is hard to reproduce (I have a lot of files called newsXXXX.nws.Z)
but annoying.  All programs dealing with directory slots should be aware
that 14-character filenames are not null terminated. This sort of bug should
have been fixed in 1.3 :-(

	The du.c.cdif file fixes this bug by changing the makedname()
routine to copy a maximum of 14 character filenames.

MY IMPROVEMENT:

	Sometimes I like to do a du, and only see the sizes for the topmost
directories.  For example, on a system where /users contains one directory
for each user, and each user may have subdirectories, I only want to see
the total amount of space used by each user.

	This is a fairly trivial change to du. It involves specifying the
required number of levels of space to print.  Each time dodir() is called
recursively, it is passed a decreasing level count.  The total line is
only printed if the level is 0 or greater.  File size lines (-a option)
are only printed if the level is greater than 0.  The output is intuitive
here, as 'du -a -l 2' will print (not in this order though) sizes for the
current directory, files in the current directory, files in all
sub-directories, all sub-directories, and all sub-sub-directories.

	I don't expect this change to become part of 1.4b, as it is non
standard.  But it is useful nevertheless, and transparent to the user.

	du.c included contains the bugfix as well as the above improvement.

Nick Andrew (nick@nswitgould.cs.nswit.oz)

--------------------------- hack here --------------------------------------
echo x - du.1
sed '/^X/s///' > du.1 << '/'
XCommand:   du - print disk usage
XSyntax:    du [-a] [-s] [-l levels] [directory]
XFlags:     -a		Print sizes of files as well as directories
X	   -s		Summary total only
X	   -l levels	Print stats for 'levels' deep (levels>=0)
XExample:
X   _D_u examines a directory and prints the amount of space occupied by the
Xfiles in that directory and its subdirectories.  If 'directory' is not
Xspecified, the disk usage for the current directory is printed.  If the
X'-l levels'  parameter is specified, sizes are only printed for the
Xspecified number of directory levels (Eg: 'du -l 1' prints the sizes of
Ximmediate subdirectories and current directory only; 'du -l 0' is identical
Xto 'du -s').
/
echo x - du.c.cdif
sed '/^X/s///' > du.c.cdif << '/'
X*** /minix/commands/du.c	Sun Oct 23 22:28:44 1988
X--- du.c	Fri Mar 24 17:10:58 1989
X***************
X*** 2,7 ****
X--- 2,9 ----
X  
X  /*
X   *	du.c		1.1	27/5/87		agc	Joypace Ltd.
X+  *			1.2	24 Mar 89	nick@nswitgould.oz
X+  *	Fixed bug involving 14 character long filenames
X   *
X   *	Copyright 1987, Joypace Ltd., London UK. All rights reserved.
X   *	This code may be freely distributed, provided that this notice
X***************
X*** 112,119 ****
X  }
X  
X  /*
X!  *	makedname - make the direcory entry from the directory name,
X!  *	and the file name, placing it in out. If this would overflow,
X   *	return 0, otherwise 1.
X   */
X  int makedname(d, f, out, outlen)
X--- 114,121 ----
X  }
X  
X  /*
X!  *	makedname - make the pathname from the directory name, and the
X!  *	directory entry, placing it in out. If this would overflow,
X   *	return 0, otherwise 1.
X   */
X  int makedname(d, f, out, outlen)
X***************
X*** 123,136 ****
X  int	outlen;
X  {
X    char	*cp;
X! 
X!   if (strlen(d) + strlen(f) + 2 > outlen)
X  	return(0);
X    for (cp = out ; *d ; *cp++ = *d++)
X  	;
X    if (*(cp-1) != '/')
X  	*cp++ = '/';
X!   while (*f)
X  	*cp++ = *f++;
X    *cp = '\0';
X    return(1);
X--- 125,143 ----
X  int	outlen;
X  {
X    char	*cp;
X!   int	length;
X! 
X!   /* find length (1-14) of directory entry */
X!   cp = f;
X!   for (length=0; *cp && (length<14); ++cp) ++length;
X! 
X!   if (strlen(d) + length + 2 > outlen)
X  	return(0);
X    for (cp = out ; *d ; *cp++ = *d++)
X  	;
X    if (*(cp-1) != '/')
X  	*cp++ = '/';
X!   while (length--)
X  	*cp++ = *f++;
X    *cp = '\0';
X    return(1);
/
echo x - du.c
sed '/^X/s///' > du.c << '/'
X/* du - report on disk usage		Author: Alistair G. Crooks */
X
X/*
X *	du.c		1.1	27/5/87		agc	Joypace Ltd.
X *			1.2	24 Mar 89	nick@nswitgould.oz
X *			1.3	31 Mar 89	nick@nswitgould.oz
X *
X *	Copyright 1987, Joypace Ltd., London UK. All rights reserved.
X *	This code may be freely distributed, provided that this notice
X *	remains attached.
X *
X *	du - a public domain interpretation of du(1).
X *
X *  1.2: 	Fixed bug involving 14 character long filenames
X *  1.3:	Add [-l levels] option to restrict printing.
X *
X */
X
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <minix/blocksize.h>
X
Xchar *prog;				/* program name */
Xchar *optstr = "asl:";			/* -a and -s arguments */
Xint silent = 0;				/* silent mode */
Xint all = 0;				/* all directory entries mode */
Xchar *startdir = ".";			/* starting from here */
Xint levels = 255;			/* # of directory levels to print */
X
X#define	LINELEN 256
X
X#define DIRNAMELEN 14
X#define	LSTAT stat
Xtypedef struct _dirstr {
X  int	inum;
X  char	d_name[DIRNAMELEN];
X} DIR;
XDIR dir;
X#define ino_t unsigned short
X
Xtypedef struct _alstr {
X  int al_dev;
X  ino_t	al_inum;
X} ALREADY;
X
X#define	MAXALREADY	50
XALREADY already[MAXALREADY];
Xint alc = 0;
X
X/*
X *	myindex - stop the scanf bug
X */
Xchar *myindex(s, c)
Xregister char	*s;
Xregister char	c;
X{
X  for (; *s; s++)
X	if (*s == c)
X		return(s);
X  return(NULL);
X}
X
X
X/*
X *	getopt - parse the arguments given.
X *	retrieved from net.sources
X */
Xint	opterr = 1;
Xint	optind = 1;
Xint	optopt;
Xchar	*optarg;
X
X#define BADCH	(int)'?'
X#define EMSG	""
X#define TELL(s)	fputs(*nargv, stderr); fputs(s, stderr);\
X	fputc(optopt, stderr); fputc('\n', stderr);\
X	return(BADCH);
X
Xint getopt(nargc, nargv, ostr)
Xint	nargc;
Xchar	**nargv;
Xchar	*ostr;
X{
X  register char	*oli;
X  static char	*place = EMSG;
X
X  if (!*place) {
X	if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place)
X		return(EOF);
X	if (*place == '-') {
X		++optind;
X		return(EOF);
X	}
X  }
X  if ((optopt = (int)*place++) == (int)':' || !(oli = myindex(ostr, optopt))) {
X	if (!*place)
X		++optind;
X	TELL(": illegal option -- ");
X  }
X  if (*++oli != ':') {
X	optarg = NULL;
X	if (!*place)
X		++optind;
X  } else {
X	if (*place)
X		optarg = place;
X	else if (nargc <= ++optind) {
X		place = EMSG;
X		TELL(": option requires an argument -- ");
X	} else
X		optarg = nargv[optind];
X	place = EMSG;
X	++optind;
X  }
X  return(optopt);
X}
X
X/*
X *	makedname - make the pathname from the directory name, and the
X *	directory entry, placing it in out. If this would overflow,
X *	return 0, otherwise 1.
X */
Xint makedname(d, f, out, outlen)
Xchar	*d;
Xchar	*f;
Xchar	*out;
Xint	outlen;
X{
X  char	*cp;
X  int	length;
X
X  /* find length (1-14) of directory entry */
X  cp = f;
X  for (length=0; *cp && (length<14); ++cp) ++length;
X
X  if (strlen(d) + length + 2 > outlen)
X	return(0);
X  for (cp = out ; *d ; *cp++ = *d++)
X	;
X  if (*(cp-1) != '/')
X	*cp++ = '/';
X  while (length--)
X	*cp++ = *f++;
X  *cp = '\0';
X  return(1);
X}
X
X/*
X *	done - have we encountered (dev, inum) before? Returns 1 for yes,
X *	0 for no, and remembers (dev, inum).
X */
Xint done(dev, inum)
Xint	dev;
Xino_t	inum;
X{
X  register ALREADY	*ap;
X  register int		i;
X  int			ret = 0;
X
X  for (i = 0, ap = already ; i < alc ; ap++, i++)
X	if (ap->al_dev == dev && ap->al_inum == inum) {
X		ret = 1;
X		break;
X	}
X  if (alc < MAXALREADY) {
X	already[alc].al_dev = dev;
X	already[alc++].al_inum = inum;
X  }
X  return(ret);
X}
X
X/*
X *	dodir - process the directory d. Return the long size (in blocks)
X *	of d and its descendants.
X */
Xlong dodir(d,thislev)
Xchar	*d;
Xint	thislev;
X{
X  struct stat	s;
X  long		total = 0L;
X  char		dent[LINELEN];
X  int		fd;
X  
X  if ((fd = open(d, 0)) < 0)
X	return(0L);
X  while (read(fd, &dir, sizeof(dir)) > 0) {
X	if (strcmp(dir.d_name, ".") == 0 ||
X	    strcmp(dir.d_name, "..") == 0)
X		continue;
X	if (dir.inum == 0)
X		continue;
X	if (!makedname(d, dir.d_name, dent, sizeof(dent)))
X		continue;
X	if (LSTAT(dent, &s) < 0)
X		continue;
X	if (s.st_nlink > 1 && done(s.st_dev, s.st_ino))
X		continue;
X	if ((s.st_mode & S_IFMT) == S_IFDIR)
X		total += dodir(dent,thislev-1);
X	switch(s.st_mode & S_IFMT) {
X	case S_IFREG:
X	case S_IFDIR:
X		total += (s.st_size + BLOCK_SIZE) / BLOCK_SIZE;
X		break;
X	}
X	if (all && (s.st_mode & S_IFMT) != S_IFDIR)
X		if (thislev>0) /* this is correct - file in subdir */
X		printf("%D\t%s\n", (s.st_size + BLOCK_SIZE) / BLOCK_SIZE, dent);
X  }
X  close(fd);
X  if (!silent)
X	if (thislev>=0) /* this is correct - subdir itself */
X		printf("%D\t%s\n", total, d);
X  return(total);
X}
X
X/*
X *	OK, here goes...
X */
Xmain(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X  long	tot;
X  int	c;
X
X  prog = argv[0];
X  while ((c = getopt(argc, argv, optstr)) != EOF)
X	switch(c) {
X	case 'a' :
X		all = 1;
X		break;
X	case 's' :
X		silent = 1;
X		break;
X	case 'l':
X		levels = atoi(optarg);
X		break;
X	default :
X		fprintf(stderr, "Usage: %s [-a] [-s] [-l levels] [startdir]\n", prog);
X		exit(1);
X	}
X  if (optind < argc)
X	startdir = argv[optind];
X  tot = dodir(startdir,levels);
X  if (silent)
X	printf("%D\t%s\n", tot, startdir);
X  exit(0);
X}
/
echo x - README
sed '/^X/s///' > README << '/'
XManifest:
X	README		This file
X	du.1		Revised manual page for du
X	du.c.cdif	Cdiff against Minix 1.4a du.c to fix filenames bug
X	du.c		du.c version 1.3 with -l parameter
X
X"to du or not to du, that is the question"	2du | ~2du => q
X
XDU 1.4a BUG:
X
X	Minix 1.4a du.c has a bug where it may incorrectly report the space
Xoccupied by a directory if that directory contains filenames of length 14.
XThis bug is hard to reproduce (I have a lot of files called newsXXXX.nws.Z)
Xbut annoying.  All programs dealing with directory slots should be aware
Xthat 14-character filenames are not null terminated. This sort of bug should
Xhave been fixed in 1.3 :-(
X
X	The du.c.cdif file fixes this bug by changing the makedname()
Xroutine to copy a maximum of 14 character filenames.
X
XMY IMPROVEMENT:
X
X	Sometimes I like to do a du, and only see the sizes for the topmost
Xdirectories.  For example, on a system where /users contains one directory
Xfor each user, and each user may have subdirectories, I only want to see
Xthe total amount of space used by each user.
X
X	This is a fairly trivial change to du. It involves specifying the
Xrequired number of levels of space to print.  Each time dodir() is called
Xrecursively, it is passed a decreasing level count.  The total line is
Xonly printed if the level is 0 or greater.  File size lines (-a option)
Xare only printed if the level is greater than 0.  The output is intuitive
Xhere, as 'du -a -l 2' will print (not in this order though) sizes for the
Xcurrent directory, files in the current directory, files in all
Xsub-directories, all sub-directories, and all sub-sub-directories.
X
X	I don't expect this change to become part of 1.4b, as it is non
Xstandard.  But it is useful nevertheless, and transparent to the user.
X
X	du.c included contains the bugfix as well as the above improvement.
/
exit 0
-- 
			"Zeta Microcomputer Software"
ACSnet:    nick@nswitgould.oz
UUCP:      ...uunet!munnari!nswitgould.oz!nick
Fidonet:   Nick Andrew on 713/602 (Zeta), nick@nswitgould on 713/603 (ACSgate)