/* Sizer.c

   by Fabbian G. Dufoe, III
      350 Ling-A-Mor Terrace South
      St. Petersburg, Florida  33705
      813-823-2350

      GENIE: F.DUFOE3

   This software is in the public domain.  You may use it any way you wish.

   Sizer reports the number of bytes and blocks in a disk, file, or directory
   and all included subdirectories.  The user selects the Sizer icon and uses
   extended selection to indicate the other files and directories he wants
   sized.  Sizer will open a workbench window with the number of blocks and
   the number of bytes.  To terminate the program the user clicks on the
   window's close gadget.

   Revison summary:
   15 October 1991: Initial release.

   18 May 1992:
      Fixed NULL pointer bug in GetSize() function.  Function was passing a
      NULL pointer when it should have been passing a pointer to a NULL
      string.

   13 June 1992:
      Added code for reporting blocks for old file system (OFS) and fast
      file system (FFS) disks.
      Made CountBlocks() and GetSize() static and moved their prototypes
      into this file.
      Added code to update display for directory sizes as well as files.
*/

#include <workbench/startup.h>
#include <string.h>
#include "Sizer.h"

/* Prototypes for functions defined in Sizer.c */

#ifndef __NOPROTO
#ifndef __PROTO
#define __PROTO(a) a
#endif
#else
#ifndef __PROTO
#define __PROTO(a) ()
#endif
#endif

static long CountBlocks __PROTO((int blocksize,
                                 LONG bytes));
static void GetSize __PROTO((BPTR lock,
                             char *name,
                             struct record *Record));

void
main(int argc,
     char **argv)
{
   struct WBStartup *argmsg;
   int i;
   struct FileInfoBlock IconBlock;  /* Information about ".info" file */
   BPTR ParentLock;
   struct record Record =
   {
      NORM,    /* code      */
      0,       /* bytes     */
      0,       /* OFSblocks */                                                /* 13 June 1992 */
      0,       /* FFSblocks */                                                /* 13 June 1992 */
      0,       /* files     */
      0        /* dirs      */
   };
   struct WBArg *wb_arg;

   if (argc == 0) /* Started from Workbench */
   {
      /* Since the program was started from the Workbench argv contains a
         pointer to the Workbench startup message.  We'll save that pointer
         in argmsg.  The Workbench startup message contains a pointer to an
         array of arguments.  That pointer is in sm_ArgList.  Each argument
         has a lock and a name.  The first argument is the program itself.
         The number of arguments is stored in sm_NumArgs.
      */
      argmsg = (struct WBStartup *)argv;
      wb_arg = argmsg->sm_ArgList;

      /* We'll try to open the input/output window.  We'll continue only if
         we're successful.
      */
      SizerIO(&Record);
      if (Record.code == NORM)
      {
         for (i = 1; i < argmsg->sm_NumArgs; i++)
         {
            /* We want to include the size of the icon file in our
               calculations.  If the user selected a directory object we'll
               have to Examine() it to find out its name, then switch to its
               parent directory, append ".info" to the file name, and
               Examine() the icon file to get its size.
            */
            if (*(wb_arg + i)->wa_Name == NULL)  /* Directory object */
            {
               if (Examine((wb_arg + i)->wa_Lock, &IconBlock) == 0)
               {
                  Record.code = FAIL;
                  SizerIO(&Record);
                  break;
               }
               else
               {
                  if (0 != (ParentLock = ParentDir((wb_arg + i)->wa_Lock)))
                  {
                     (void)strcat(IconBlock.fib_FileName, ".info");
                     GetSize(ParentLock, IconBlock.fib_FileName, &Record);
                     UnLock(ParentLock);
                  }
               }
            }
            else
            {
               strcpy(IconBlock.fib_FileName, (wb_arg + i)->wa_Name);
               (void)strcat(IconBlock.fib_FileName, ".info");
               GetSize((wb_arg + i)->wa_Lock,
                       IconBlock.fib_FileName,
                       &Record);
            }
            GetSize((wb_arg + i)->wa_Lock,
                    (wb_arg + i)->wa_Name,
                    &Record);
         }
         if (Record.code == NORM)
         {
            Record.code = DONE;
            SizerIO(&Record);
         }
         Record.code = CLOSEIO;
         SizerIO(&Record);
      }
   }
}


static long                                                                   /* 13 June 1992 */
CountBlocks(int blocksize,
            LONG bytes)
/* FUNCTION
      This function calculates the number of disk blocks needed by a file
      based on the number of bytes in the file and the number of bytes in a
      block.  The number of blocks is the sum of the File Header Block, the
      data blocks, and any File List Blocks.  Data blocks are calculated by
      dividing the number of bytes by the block size and adding another data
      block for bytes left over (if there is a remainder).  Every file has
      one File Header Block, which can accommodate up to 72 data blocks.  If
      there are more data blocks, a File List Block is required for the file
      extension.  The File List Block can accommodate 72 data blocks.  There
      may be as many File List Blocks as needed for the data blocks.  If
      there are no bytes in the file it is either an empty file or a
      directory.  Either one requires a single block.

   INPUTS
      blocksize   The number of bytes in a block
      bytes       The number of bytes in the file

   RESULTS
      The function returns the number of blocks the file will require.  The
      minimum value is 1.
*/
{
   long blocks;

   if (bytes > 0)
   {
      blocks = bytes / blocksize;
      if ((bytes % blocksize) != 0)
      {
         blocks++;
      }
      if ((blocks % 72) == 0)
      {
         blocks += blocks / 72;
         blocks--;
      }
      else
      {
         blocks += blocks / 72;
      }
      blocks += 1;
   }
   else
   {
      blocks = 1;
   }
   return(blocks);
}


static void                                                                   /* 13 June 1992 */
GetSize(BPTR lock,
        char *name,
        struct record *Record)
{
   struct FileInfoBlock infoBlock;
   BPTR objlock;
   BPTR olddir;

   if (Record->code == FAIL)
      return;

   olddir = CurrentDir(lock);
   if (*name == NULL)    /* Directory object */
   {
      /* Examine everything in the directory and accumulate the sizes. */
      if (Examine(lock, &infoBlock) == 0)
      {
         Record->code = FAIL;
         SizerIO(Record);
      }
      else
      {
         Record->bytes += infoBlock.fib_Size;
         Record->OFSblocks += CountBlocks(488, 0);                            /* 13 June 1992 */
         Record->FFSblocks += CountBlocks(512, 0);                            /* 13 June 1992 */
         Record->dirs++;                                                      /* 13 June 1992 */
         while ((ExNext(lock, &infoBlock) != 0) && (Record->code == NORM))
         {
            if (infoBlock.fib_DirEntryType > 0) /* Directory */
            {
               if ((objlock = Lock(infoBlock.fib_FileName, ACCESS_READ))
                   == 0)
               {
                  Record->code = FAIL;
                  SizerIO(Record);
               }
               else
               {
                  GetSize(objlock, "\0", Record);                             /* 18 May 1992 */
                  UnLock(objlock);
                  SizerIO(Record);                                            /* 13 June 1992 */
               }
            }
            else
            {
               Record->bytes += infoBlock.fib_Size;
               Record->OFSblocks += CountBlocks(488, infoBlock.fib_Size);     /* 13 June 1992 */
               Record->FFSblocks += CountBlocks(512, infoBlock.fib_Size);     /* 13 June 1992 */
               Record->files++;
               SizerIO(Record);
            }
         }
      }
   }
   else  /* Plain file */
   {
      if ((objlock = Lock(name, ACCESS_READ)) == 0)
      {
         Record->code = FAIL;
         SizerIO(Record);
      }
      else
      {
         if (Examine(objlock, &infoBlock) == 0)
         {
            Record->code = FAIL;
            SizerIO(Record);
         }
         else
         {
            Record->bytes += infoBlock.fib_Size;
            Record->OFSblocks += CountBlocks(488, infoBlock.fib_Size);        /* 13 June 1992 */
            Record->FFSblocks += CountBlocks(512, infoBlock.fib_Size);        /* 13 June 1992 */
            Record->files++;
            SizerIO(Record);
         }
         UnLock(objlock);
      }
   }
   (void)CurrentDir(olddir);
   return;
}
