[comp.sys.ibm.pc] Disk Use Utility

tom@cogpsi.UUCP (Tom Vijlbrief) (05/08/87)

This (MSC 4.00) C program implements a Un*x like du program.
It computes the size of (sub)directory trees.

Tom Vijlbrief
TNO Institute for Perception
P.O. Box 23			Phone: +31 34 63 14 44
3769 DE  Soesterberg		E-mail: tnosoes!cogpsi!tom@mcvax.cwi.nl
The Netherlands				{seismo|...}!mcvax!tnosoes!cogpsi!tom

PS: It would be nice if someone ON EUNET send me the sources of the Unix
    patch program, Thanks.

C source follows:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/*
 * du.c:
 *
 * `Du' is a disk usage utility which computes the diskusage of
 * all the files in a directory and it's subdirectories.
 *
 * This utility is comparable with the UNIX du program.
 *
 * Usage: du [-s] [-a] [d:path ...]
 *
 * Option -s (/s) does not show subdirectories (short listing).
 * Option -a (/a) shows size of all the files.
 *
 * Copyright: Tom Vijlbrief, Baarn, The Netherlands
 * Date:      Fri May  8 13:53:29 GMT+1:00 1987
 *
 * This program may be distributed freely for non commercial purposes
 * and with this Copyright notice unchanged.
 */

/*
 * Compilation must be performed with structure packing and larger stack:
 * Microsoft C 4.00: -Zp -F2000
 */

#include	<stdio.h>
#include	<string.h>
#include	<dos.h>
#include	<io.h>
#include	<direct.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<ctype.h>

#define		BLOCK_SIZE	512
#define		ALL_FILES	(0x1 | 0x2 | 0x4 | 0x10 |0x20)
#define		DIRECTORIES	(0x10)

#define		NAME_LEN	8
#define		EXT_LEN		3

#define		MAX_CWD		256

typedef unsigned char	byte;

typedef struct {
	byte		FF;
	byte		zero_5[5];
	byte		attribute;
	byte		drive;
	char		name[NAME_LEN];
	char		extension[EXT_LEN];
	unsigned	cur_block;
	unsigned	rec_size;
	unsigned	fs_low;
	unsigned	fs_high;
	unsigned	date;
	byte		unused2[10];
	byte		cur_record;
	unsigned	ran_low;
	unsigned	ran_high;
} ext_fcb;

char	*get_first(char *, int, ext_fcb *);
char	*get_next(ext_fcb *);
long	scan();


byte	sector[BLOCK_SIZE];

int	cluster_size;

int	a_flag;
int	s_flag;
int	in_tree;

ext_fcb	return_fcb;

main(argc, argv)

int	argc;
char	*argv[];
{
  char		*option_p;
  static char	cur_cwd[MAX_CWD];
  int		drive_nr;
  int		cur_drive;

  while (argc > 1) {
	option_p= *++argv;

	if (*option_p == '-' || *option_p == '/') {
		for (option_p++; *option_p; option_p++) {
			switch (*option_p) {
			case 'a':
				a_flag++;
				break;
			case 's':
				s_flag++;
				break;
			default:
				fprintf(stderr,
					"Unknown option: %c\n", *option_p);
				fprintf(stderr,
					"Usage: du [-s] [-a] [d:path ...]\n");
				exit(1);
			}
		}
		argc--;
	} else
		break;
  }

  if (argc == 1) {
	cluster_size= comp_cluster(get_drive());
	scan();
  } else {
	for (; argc > 1; argc--, argv++) {
		cur_drive= get_drive();
		getcwd(cur_cwd, MAX_CWD - 1);
		if ((*argv)[1] == ':')
			set_drive(tolower((*argv)[0]) - 'a');
		if (chdir(*argv) != 0) {
			perror(*argv);
			set_drive(cur_drive);
			continue;
		}
		cluster_size= comp_cluster(get_drive());
		scan();
		set_drive(cur_drive);
		if (chdir(cur_cwd) != 0) {
			perror(cur_cwd);
			exit(1);
		}

		in_tree= 0;
	}
  }
}


static struct stat	the_stat;
static char		cwd[MAX_CWD];

long scan()
{
  char		*p;
  long		file_size;
  long		disk_use;
  int		tree_flag;
  ext_fcb	the_fcb;
  char		pattern[NAME_LEN + EXT_LEN + 2];

  tree_flag= in_tree;
  disk_use= 0;

  strcpy(pattern, "????????.???");

  p= get_first(pattern, ALL_FILES, &the_fcb);
  for (; p != NULL; p= get_next(&the_fcb)) {
	if (strcmp(p, "..      .   ") == 0
	   || strcmp(p, ".       .   ") == 0)
		continue;

	if (stat(p, &the_stat) != 0) {
		perror(p);
		exit(1);
	}

	if (the_stat.st_mode & S_IFDIR) {
		if (a_flag) {
			printf("%s\n", p);
			printf("========================\n");
		}
		chdir(p);
		in_tree= 1;
		disk_use+= scan();
		chdir("..");
	} else {
		disk_use+= ((file_size= the_stat.st_size) + (cluster_size - 1))
			/ cluster_size * cluster_size;
		if (a_flag) {
			printf("%s: %10ld\n", p, file_size);
		}
	}
  }

  if (a_flag)
	  printf("========================\n");
  if (!s_flag || !tree_flag)
	  printf("Disk Usage  : %10ld (%s)\n", disk_use,
		getcwd(cwd, MAX_CWD - 1));
  if (a_flag)
	  printf("\n");

  return(disk_use);
}


/*
 * get_first returns the first_name in a directory matching the
 * pattern (e.g. ????????.OBJ) and attribute
 */

char *get_first (pattern, attribute, fcb_p)

char	*pattern;
int	attribute;
ext_fcb	*fcb_p;
{
  set_dta(&return_fcb);

  strncpy(fcb_p->name, pattern, NAME_LEN);
  strncpy(fcb_p->extension, pattern + NAME_LEN + 1, EXT_LEN);
  fcb_p->attribute= attribute;
  fcb_p->FF= 0xFF;
  fcb_p->drive= get_drive() + 1;

  if ((bdos(0x11, (int) fcb_p, 0) & 0xFF) == 0) {
	strncpy(pattern, return_fcb.name, NAME_LEN);
	strncpy(pattern + NAME_LEN + 1, return_fcb.extension, EXT_LEN);
	return(pattern);
  } else
	return(NULL);
}


char *get_next(fcb_p)

ext_fcb	*fcb_p;
{
  static char	pattern[]= "12345678.123";

  set_dta(&return_fcb);

  if ((bdos(0x12,  (int) fcb_p, 0) & 0xFF) == 0) {
	strncpy(pattern, return_fcb.name, NAME_LEN);
	strncpy(pattern + NAME_LEN + 1, return_fcb.extension, EXT_LEN);
	pattern[NAME_LEN + EXT_LEN + 1]= '\0';
	return(pattern);
  } else {
	return(NULL);
  }
}


/*
 * `set_drive' changes the current drive. (A = 0, etc).
 */
int set_drive(drive_nr)

int	drive_nr;
{
  return(bdos(0xE, drive_nr, 0) & 0xFF);
}

/*
 * `get_drive' returns the number of the current drive. (A = 0, etc).
 */
int get_drive()

{
  return(bdos(0x19, 0, 0) & 0xFF);
}



set_dta(p)

ext_fcb	*p;
{
  return(bdos(0x1A, (int) p, 0) & 0xFF);
}



#define		DISK		0x13
#define		READ_SECT	0x2
#define		RESET_DISK	0x0

#define		FLOP_RETRIES	3

int readsect(buffer, drive, head, cyl, sector, n_sectors)

char		*buffer;
unsigned int	drive;
unsigned int	head;
unsigned int	cyl;
unsigned int	sector;
unsigned int	n_sectors;
{
  int		result;
  int		retries;
  int		max_retries;
  union REGS	inregs,
		outregs;
  struct SREGS	segregs;

  segread(&segregs);
  segregs.es= segregs.ds;

  if (drive < 0x80)
	max_retries= FLOP_RETRIES;
  else
	max_retries= 1;

  for (retries= 0; retries < max_retries; retries++) {
	if (drive < 0x80) {
		inregs.h.ah= RESET_DISK;
		inregs.h.dl= drive;
		int86(DISK, &inregs, &outregs);
	}

	inregs.h.ah= READ_SECT;
	inregs.x.bx= (unsigned) buffer;
	inregs.h.dl= drive;
	inregs.h.dh= head;
	inregs.h.ch= cyl;
	inregs.h.cl= sector | ((cyl & ~0xFF) >> 2);
	inregs.h.al= n_sectors;

	result= (unsigned) int86x(DISK, &inregs, &outregs, &segregs) >> 8;

	if (outregs.x.cflag)
		continue;
	else
		break;
  }
  if (retries == max_retries)
	return(-result);
  else
	return(0);
}



int comp_cluster(drive_nr)

int	drive_nr;
{
  int	nr_sectors;
  int	head_nr;
  int	high_head;
  int	sector_nr;

  if (drive_nr > 1)
	drive_nr+= 0x80 - 2;

  high_head= max_head(drive_nr);

  for (head_nr= 0; head_nr <= high_head; head_nr++) {
    for (sector_nr= 1; sector_nr <= 8; sector_nr++) {
	if (readsect(sector, drive_nr, head_nr, 0, sector_nr, 1) != 0) {
		fprintf(stderr, "Cannot read bootblock from disk\n");
		exit(1);
	}
	if (sector[0] == 0xEB)
		break;
    }
    if (sector[0] == 0xEB)
	break;
  }
  if (sector_nr > 8 || (nr_sectors= sector[13]) > 16) {
	nr_sectors= 4;
	fprintf(stderr,
	    "Unknown bootblock, clustersize of 4 sectors assumed\n");
  }
  if (nr_sectors == 0)
	nr_sectors= 2;
  return(nr_sectors * BLOCK_SIZE);
}



#define		GET_PARAM	0x8

int max_head(drive)

unsigned int	drive;
{
  union REGS	inregs,
		outregs;

  inregs.h.ah= GET_PARAM;
  inregs.h.dl= drive;

  int86(DISK, &inregs, &outregs);

  if (outregs.x.cflag)
	return(0);
  else
	return(outregs.h.dh);
}