AUSAM/source/libc/tdu.c

#
/*
**	Tdu calculates total disk usage (inodes and blocks) for the given directory
**
**		the total number of files is available in the global "t_files"
**		and likewise the total number of blocks in "t_blocks"
*/



#include	<local-system>
#include	<passwd.h>
#include	<stat16.h>

struct	statbuf	__statb;

extern	errno;
int	__derror;
long	_tdu();
long	_malformed();
char	*_nextpath();
unsigned _size();
long	t_blocks;
long	t_files;
char	__pathname[200];
#define	PATHEND	(&__pathname[sizeof __pathname])

struct { int hiword; unsigned loword; };



tdu( dir )
  register char *dir;		/** pointer to directory to be totalled **/
{
	int			parino;
	/*
	**	get inode of parent directory of "dir" (N.B. ".." is not good enough)
	*/
	{
		register char	*dp, *np;
		char		dbuf[SSIZ];

		if ( (np = dir) == 0 || *np == 0 )
			np = "/";

		for ( dp = dbuf ; *dp++ = *np++ ; )
			if ( dp >= &dbuf[SSIZ] )
			{
				printf( "%s: too long!\n", dir );
				return( -1 );
			}

		while ( *--dp != '/' )
			if ( dp <= dbuf )
			{
				printf( "Bad directory path: %s\n", dir );
				return( -1 );
			}

		if ( dp == dbuf )	/* root! */
			dp++;
		*dp = 0;

		if ( newstat( dbuf, &__statb ) < 0 )
		{
			printf( "Parent directory: " );
			perror( dbuf );
			return( -1 );
		}

		parino = __statb.sb_inumber;
	}

	/*
	**	move over to the user's home directory
	*/
	if ( newstat( dir , &__statb ) < 0 || chdir(dir) < 0 )
	{
		perror( dir );
		return( -1 );
	}

	__derror = 0;
	errno = 0;
	t_blocks = 0;
	t_files = 0;
	return( _tdu( _nextpath( __pathname, dir ), parino ) >> 16 );	/* N.B. __statb set up above */
}




#define	ERROR	0XFFFFFFFFL

struct
{
	int f_ino;
	char f_name[14];
	char f_end;
}
	__dirbuf;

/*
**	"tdu" returns the disc usage of the current directory
**	      and all sub-directories.
**		Inodes are weighted at IWEIGHT blocks.
**		Links are shared equally.
*/

long _tdu( pathend, parino )
  char *pathend;
  unsigned parino;
{
	register	fd, i, posn;
	long		inodes, blocks, t;
	unsigned	dino;

  if ( pathend == 0 )
	return( ERROR );

  dino = __statb.sb_inumber;

  inodes = 0X10000;
  t_files =+ 0X10000;
  blocks.loword = 0;
  blocks.hiword = _size();
  t_blocks =+ blocks;

  posn = 0;
  __dirbuf.f_end = '\0';

loop:
  if ( ((fd = open( __pathname , 0)) >= 0) && (seek( fd , posn , 0) >= 0) )
  {
	while ( (i = read( fd , &__dirbuf , 16 )) == 16 )
	{
		posn =+ i;

		if ( __dirbuf.f_ino )
			if ( newstat( __dirbuf.f_name , &__statb ) >= 0 )
			{
				if ( (__statb.sb_flags & IFTYP) == IFDIR )
				{
					if ( __statb.sb_inumber != dino && __statb.sb_inumber != parino )
					{
						if ( _dots() )
							return( _malformed( "separate directory!" ) );

						if ( posn <= 32 )
							return( _malformed( "\".\" or \"..\" displaced!" ) );

						if ( chdir( __dirbuf.f_name ) >= 0 )
						{
							close( fd );
							blocks =+ _tdu( _nextpath( pathend, __dirbuf.f_name ), dino );
							*pathend = 0;
							chdir( __pathname );
							if ( __derror )
								return( ERROR );
							goto loop;
						}
						else
							return( _malformed() );
					}
					else
						if ( posn > 32 )
							return( _malformed( "link displaced" ) );
						else
							if ( !_dots() )
								return( _malformed( "\".\" or \"..\" renamed!" ) );
				}
				else
				{
					/* fixed point arithmetic */
					t = (0X10000 / (__statb.sb_nlinks&0377));
					t_files =+ t;
					inodes =+ t;
					t = ((0X10000 * _size()) / (__statb.sb_nlinks&0377));
					t_blocks =+ t;
					blocks =+ t;
				}
			}
			else
				return( _malformed() );
	}

	close( fd );

	if ( i )
		return( _malformed( "bad length" ) );

	return( blocks + inodes*IWEIGHT );
  }
  else
  {
	__dirbuf.f_name[0] = 0;
	return( _malformed() );
  }
}


unsigned _size()
{
	register unsigned b, ib;

  b = ((__statb.sb_size0 & 0377) << 7) + (((__statb.sb_size1 + 0777) >> 9) & 0177);
  if ( ib = b>>3 )
  {
	b++;
	if ( ib =>> 5 )
	{
		b =+ ib;	/* indirect */
		if ( ib > 8 )
			b++;	/* double indirect */
	}
  }

  return( b );
}


long _malformed( s )
  char *s;
{
	extern	errno;

  putchar( '\n' );
  printf( "MALFORMED DIRECTORY in %s\n\t", __pathname );
  if ( errno )
	perror( __dirbuf.f_name );
  else
	printf( "%s: %s\n", __dirbuf.f_name, s );
  __derror++;
  return( ERROR );
}


_dots()
{
	register char	*sp = __dirbuf.f_name;

	if ( *sp++ == '.'
	     && ( *sp == '\0'
		  || ( *sp++ == '.' && *sp == '\0' )
		)
	   )
		return( 1 );

	return( 0 );
}


char *_nextpath( at, name )
  register char *at, *name;
{
  if ( *name != '/' )
	*at++ = '/';

  do
	if ( at >= PATHEND )
	{
		__derror++;
		return( 0 );
	}
  while
	( *at++ = *name++ );

  return( --at );
}