
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/xpk.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <dos.h>

#define MAXLINES 2000

static char ver[]= "$VER: xDir 1.1 (" __DATE__ ")\r\n";

struct Library *XpkBase;
char errbuf[XPKERRMSGSIZE + 1], *Line[MAXLINES];
struct FileInfoBlock *Fib;
long utot, ctot, Lines;
char buf[300];

void exitem (char *name);
void exfile (struct FileInfoBlock *fib);
void print (char *str);
void showlines (void);

void 
end (char *text)
{
  if (text)
    print (text);
  if (XpkBase)
    CloseLibrary (XpkBase);
  _exit (text ? 10 : 0);
}

int 
breakfunc (void)
{
  end ("***Break\n");
  return 0;
}

char usage[]= "Usage: xDir files/dirs\n";

void 
main (int argc, char *argv[])
{
  int i, ratio = 0;

  onbreak (breakfunc);

  if (argc == 2 && !stricmp (argv[1], "?"))
    end (usage);

  if (!(XpkBase = OpenLibrary (XPKNAME, 0)))
    end ("Cannot open " XPKNAME "\n");

  if (argc < 2)
    argv[1] = "", argc = 2;

  if (!(Fib = AllocMem (sizeof (*Fib), 0)))
    end ("Not enough memory\n");

  if (*argv[1] == '-')
    end (usage);

  print ("Original  Packed  Ratio Type Protection Name\n");
  print ("-------- -------- ----- ---- ---------- ----\n");
  for (i = 1; i < argc; i++)
    exitem (argv[i]);

  showlines ();

  if (utot)
    ratio = 100 - 100 * ctot / utot;
  print ("-------- -------- -----\n");
  sprintf (buf, "%8ld %8ld  %2ld%%\n", utot, ctot, ratio);
  print (buf);

  end (NULL);
}

void 
showlines (void)
{
  int i;

  for (i = 0; i < Lines; i++) {
    Write (Output (), Line[i], strlen (Line[i]));
    if (SetSignal (0, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
      end (" *** Break\n");
  }
  Lines = 0;
}

void 
print (char *str)
{
  Write (Output (), str, strlen (str));
}

char *
dirname (char *rp)
{
  static char dir[200];
  char *wp = dir, *lp = dir;

  while (*rp) {
    if (*rp == ':')
      lp = wp + 1;
    if (*rp == '/')
      lp = wp;
    *wp++ = *rp++;
  }
  *lp = 0;
  return dir;
}

void
exitem (char *name)
{
  BPTR lock, prev;

  if (!(lock = Lock (name, ACCESS_READ))) {
    sprintf (buf, "Error %ld reading %s\n", IoErr (), name);
    print (buf);
    return;
  }

  if (!Examine (lock, Fib)) {
    UnLock (lock);
    sprintf (buf, "Error %ld reading %s\n", IoErr (), name);
    print (buf);
    return;
  }

  if (Fib->fib_DirEntryType < 0) {
    UnLock (lock);
    if (!(lock = Lock (dirname (name), ACCESS_READ))) {
      sprintf (buf, "Error locking %s\n", name);
      print (buf);
      return;
    }

    prev = CurrentDir (lock);
    exfile (Fib);
    UnLock (CurrentDir (prev));
  }
  else {

    prev = CurrentDir (lock);

    if (Lines) {
      showlines ();
      print ("\n");
    }

    while (ExNext (lock, Fib))
      exfile (Fib);
    UnLock (CurrentDir (prev));
  }
}

struct TagItem tags[]=
{XPK_InName, NULL, XPK_PassThru, TRUE, TAG_DONE};

void
exfile (struct FileInfoBlock *fib)
{
  struct XpkFib xfib;
  char prot[10];
  int i, clen = 0, ulen = 0;
  char buf[200];

  if (SetSignal (0, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
    end (" *** Break\n");

  prot[0] = fib->fib_DirEntryType < 0 ? '-' : 'd';
  for (i = 0; i < 8; i++)
    prot[8 - i] = (Fib->fib_Protection ^ 0x0f) & (1 << i) ? "dewrapsh"[i] : '-';
  prot[9] = 0;

  if (XpkExamineTags (
		       &xfib,
		       XPK_InName, (long) fib->fib_FileName,
		       TAG_DONE
      )|| xfib.Type != XPKTYPE_PACKED) {
    if (fib->fib_EntryType < 0)
      sprintf (buf, "%8ld                      %9s %-20s\n",
	       clen = ulen = fib->fib_Size,
	       prot,
	       fib->fib_FileName);
    else
      sprintf (buf, "%8s                      %9s %-20s\n",
	       "<Dir>",
	       prot,
	       fib->fib_FileName);
  }
  else
    sprintf (buf, "%8ld %8ld  %2ld%%  %4s  %9s %-20s\n",
	     ulen = xfib.ULen,
	     clen = xfib.CLen,
	     xfib.Ratio,
	     xfib.Packer,
	     prot,
	     fib->fib_FileName);

  for (i = Lines - 1; i >= 0 && stricmp (Line[i] + 40, buf + 40) > 0; i--)
    Line[i + 1] = Line[i];
  Line[i + 1] = strcpy (malloc (strlen (buf) + 1), buf);

  if (++Lines >= MAXLINES)
    end ("Buffer overflow\n");

  utot += ulen;
  ctot += clen;
}
