#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <errno.h>
#include <windows.h>

/*

  Program to walk the directory tree (from a nominated starting point)
  and report on the size of each file and directory in the first level.

  The purpose of this is to analyse your disk and work out which directories/files
  could be deleted to free up space.

  Author: Nick Gammon  <nick@gammon.com.au>
  Web:    http://www.gammon.com.au

  Date:   8th August 1999

  Usage:  treeinfo <starting_point>

  eg.     treeinfo c:\games

*/


void PrintWin32Error (DWORD ErrorCode)
{
 
  LPVOID lpMsgBuf;
  FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |  
                 FORMAT_MESSAGE_FROM_SYSTEM |     
                 FORMAT_MESSAGE_IGNORE_INSERTS,    
                 NULL,
                 ErrorCode,
                 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                 (LPTSTR) &lpMsgBuf,    
                 0,    
                 NULL );
	printf("%s", lpMsgBuf );
	LocalFree( lpMsgBuf );
}

void process_dir (char * dirname, 
                  char * subdirname, 
                  int level,
                  __int64 * totalsize,
                  long * totalfiles)
  {
  char * thisdir = NULL;
  WIN32_FIND_DATA fileinfo;
  HANDLE hdl;
  double mb;

  // we add 4 below for:
  //   null at end: 1
  //   \ between dir and subdir name : 1
  //   \* at end: 2

  thisdir = malloc (strlen (dirname) + strlen (subdirname) + 4);
  if (!thisdir)
    {
    printf ("*** Unable to allocate memory for directory name: %s\\%s\n", 
            dirname, subdirname);
    return;
    }

  strcpy (thisdir, dirname);
  if (strlen (subdirname) > 0)
    {
    strcat (thisdir, "\\");
    strcat (thisdir, subdirname);
    }
  strcat (thisdir, "\\*");

  if (strlen (thisdir) > _MAX_PATH)
    {
    printf ("*** pathname: \"%s\" is too long to process\n", thisdir);
    free (thisdir);
    return;
    }

  hdl = FindFirstFile (thisdir, &fileinfo);
  
  thisdir [strlen (thisdir) - 2] = 0;   // remove wildcard from end

  if (hdl == INVALID_HANDLE_VALUE)
    {
    DWORD nErr = GetLastError ();

    if (level != 0 && nErr == ERROR_NO_MORE_FILES)
      {
      free (thisdir);
      return;
      }

    printf ("*** %s : ", thisdir);    
    PrintWin32Error (nErr);
    free (thisdir);
    return;
    }

  do
    {
    
    if (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
      {
      if (strcmp (".", fileinfo.cFileName) != 0 &&
          strcmp ("..", fileinfo.cFileName) != 0)
        {
        __int64 total = 0;
        long    files = 0;
        process_dir (thisdir, fileinfo.cFileName, level + 1, &total, &files);
        *totalsize += total;
        *totalfiles += files;
        }
      }
    else
      {
      LARGE_INTEGER nSize;
      nSize.HighPart = fileinfo.nFileSizeHigh;
      nSize.LowPart = fileinfo.nFileSizeLow;

      *totalsize += nSize.QuadPart;
      (*totalfiles)++;
      if (level == 0)
        {
        mb = (double) nSize.QuadPart / 1024.0 / 1024.0;
        printf ("%9.3f Mb               %s\\%s \n", mb, thisdir, fileinfo.cFileName);
        }
      }


    if (FindNextFile (hdl, &fileinfo) == 0)
      {
      DWORD nErr = GetLastError ();

      if (nErr != ERROR_NO_MORE_FILES)
        {
        printf ("*** %s : ", thisdir);    
        PrintWin32Error (nErr);
        }


      FindClose (hdl);

      if (level == 1)
        {
        mb = (double) *totalsize / 1024.0 / 1024.0;
        printf ("%9.3f Mb %6ld file%s  %s \n", 
                mb, 
                *totalfiles,
                *totalfiles == 1 ? " " : "s",
                thisdir);
        }

      free (thisdir);
      return;
      }

    } while (1);

  };


int main (int argc, char * argv [])
  {

  __int64 total = 0;
  long    files = 0;

  double mb;
  char starting_point [_MAX_PATH] = "c:";
  
  if (argc > 1)
    strcpy (starting_point, argv [1]);

// remove trailing backslash from starting point

  if (starting_point [strlen (starting_point) - 1] == '\\')
    starting_point [strlen (starting_point) - 1] = 0;

  printf ("[TREEINFO Version 1.02 Gammon Software Solutions]\n");

  if (strcmp (starting_point, "/?")     == 0 ||
      strcmp (starting_point, "/help")  == 0 ||
      strcmp (starting_point, "/HELP")  == 0 )
    {
    printf ("TREEINFO [folder]                        <-- shows disk usage for <folder>\n");
    printf ("TREEINFO [folder] | sort /R              <-- sorts output\n");
    printf ("TREEINFO [folder] | sort /R > myfile.txt <-- sorts, writes to file myfile.txt\n");
    printf ("\n");
    printf ("Written by Nick Gammon <nick@gammon.com.au>\n");
    printf ("Web: http://www.gammon.com.au\n");
    printf ("\n");
    return 0;
    }

  process_dir (starting_point, "", 0, &total, &files);
  
  mb = (double) total / 1024.0 / 1024.0;
  printf ("\n");
  printf ("%9.3f Mb %6ld file%s  %s  (total)\n", 
          mb, 
          files,
          files == 1 ? " " : "s",
          starting_point);

	return 0;

  } 