/*  µls.c     ovvero  micro ls, un piccolo ed ottimizzato sostituto di ls.
**
**    Matteo F.Cortese
**    Settembre 1997
**
**  $Header: Matteo:C/DOS/RCS/µls.c,v 1.3 1997/10/19 16:11:00 mfc Exp $
*/

#include "µls.h"

#ifdef EBUG
#define D(x)    x
#else
#define D(x)
#endif

#define ANSI_WHT    "\x1b[32m"
#define ANSI_NORM   "\x1b[39m"
#define EE_MEM      -1
#define EE_BRK      304

#define DEFAULTSHELLSIZE 78

#define PATHLEN     128
#define DIM_PATTERN 32
#define Match(x)    MatchPatternNoCase(pattern,x)

#define SUFFIX      1

/*
 *  Versione ridotta della FileInfoBlock.
 */
struct MinFib
{
    struct MinFib *Next;
    unsigned char       Name[32];
    LONG                Prot;
    LONG                Size;
    LONG                Type;   //  fib_DirEntryType
    union {
      struct {
          ULONG ULen;   // Uncompressed length
          ULONG Pack;   // Name of packer (4 chars)
          UWORD Flag;   // Password-protected
          UWORD Padd;   // Zeroed-out padding bytes
      }                Xpk;
      struct DateStamp Date;
    }                   u;
};


/*
 *  Globali
 */

struct Library *XpkBase = NULL;
int ShellSize = DEFAULTSHELLSIZE;

const STRPTR StdPat = "~(#?(.info|.bak|,b)|.#?)";
const STRPTR FormatoFile = "-%3$s %2$8ld %4$9s %5$.5s  %1$s";
const STRPTR FormatoXpk  = "x%3$s %2$8ld/%4$-8ld  %5$-5s  %1$s";
const STRPTR FormatoAltro = "%2$lc%3$s          %4$9s %5$.5s  %1$s";
const STRPTR Template = "DIR/M,P=PATTERN/K,ITSELF/S,FILES/S,DIRS/S,ALL/S,LONG/S,SS=SHELLSIZE/S,WHITE/S,XPK/S";
enum { OPTDIR, OPTPAT, OPTI, OPTF, OPTD, OPTA, OPTL, OPTS, OPTW, OPTX, NUM_OPT};


struct {
    unsigned int a : 1; /* ALL */
    unsigned int d : 1; /* DIRS */
    unsigned int f : 1; /* FILES */
    unsigned int i : 1; /* ITSELF */
    unsigned int l : 1; /* LONG */
    unsigned int s : 1; /* SHELLSIZE */
    unsigned int w : 1; /* WHITE */
    unsigned int x : 1; /* XPK */
} opzioni;

#define SetOpt(x)   opzioni. ## x = 1
#define UnsetOpt(x) opzioni. ## x = 0
#define Opt(x)      (opzioni. ## x)


#ifdef HELP_INLINE

STRPTR Help =   "Valid options:\n"
                " -a ALL\t"
                ;

#endif



/*
 *  Tipi di oggetto AmigaDOS e UNIX (fib_DirEntryType)
 *
 *  DOS    Descrizione        UNIX  Tipo/Desinenza
 *
 *    1     ROOT                1       r   :
 *    2     USER DIR            2       d   /
 *    3     SOFT-LINK           3       l   @
 *    4     HARD-LINK DIR       4       D   #
 *   10     LHA-ARCHIVE DIR     2       d   /
 *   -1     ROOT                1       r   :
 *   -3     FILE                0       -
 *   -4     HARD-LINK FILE      0       -
 *   -5     PIPE FILE           5       p   |
 *  -10     LHA-ICON FILE       0       -
 *
 *  ???     XPK'ED FILE         6       x
 *
 */
/* const STRPTR Tipi[7] =      { "-", "r", "d", "l", "D", "p", "x" }; */
/* const STRPTR Desinenze[7] = { " ", ":", "/", "@", "#", "|", " " }; */

const char Tipi[7] =      "-rdlDpx";
const char Desinenze[7] = " :/@#| ";

const char Spazi[] = "                                        ";
void *pool;


/*
 *  Prototipi
 */
int mostra2(int, struct MinFib *, int, int);
void HSort(struct MinFib **, int);
void Heapfy(struct MinFib **, int, int);
void BuildHeap(struct MinFib **, int);


/*
**|ŻŻŻŻŻŻŻŻŻŻŻŻŻ|
**|   _main()   |
**|_____________|
*/

__stkargs
void _main()
{
    struct RDArgs *rda;
    LONG argv[NUM_OPT] = {NULL, (LONG) StdPat}; /* Il resto a zero */
    int errore = 0;     // Usato da tutto il progetto.
    STRPTR dirtolist = NULL;

    /*
     *  Leggiamo i parametri.
     */
    if (rda=ReadArgs(Template, argv, NULL))
    {
        /* Lettura opzioni Amiga: */

        if (argv[OPTA]) SetOpt(a);
        if (argv[OPTD]) SetOpt(d);
        if (argv[OPTF]) SetOpt(f);
        if (argv[OPTI]) SetOpt(i);
        if (argv[OPTL]) SetOpt(l);
        if (argv[OPTS]) SetOpt(s);
        if (argv[OPTW]) SetOpt(w);
        if (argv[OPTX]) SetOpt(x);


        /* Lettura opzioni UNIX: */

        {
            STRPTR *p;
            STRPTR q;

            p = (STRPTR *) argv[OPTDIR];

            if (p)
                while (*p)
                {
                    if (**p == '-')
                        for (q = *p + 1 ; *q ; ++q)
                            switch (*q)
                            {
                                case 'a': case 'A': SetOpt(a); break;
                                case 'D':           SetOpt(d); break;
                                case 'F':           SetOpt(f); break;
                                case 'i': case 'd': SetOpt(i); break;
                                case 'l':           SetOpt(l); break;
                                case 's':           SetOpt(s); break;
                                case 'w':           SetOpt(w); break;
                                case 'x':           SetOpt(x); break;
                                default:
                            }
                    else
                    {
                        if (dirtolist)
                            errore = ERROR_TOO_MANY_ARGS;
                        else
                            dirtolist = *p;
                    }

                    ++p;
                }
        }

        if (dirtolist == NULL)
            dirtolist = "";


        /* Impostiamo la dimensione della finestra */

        if (Opt(s))
        {
            BPTR con = Output();
            if (IsInteractive(con))
            {
                /* Stiamo scrivendo su una console: usiamo le */
                /* sequenze WINDOW-STATUS-REQUEST ("<ESC>[0 q") */
                /* e WINDOW-BOUND-REPORT ("<ESC>[1;1;<h>;<w> r") */

                unsigned char WSR[4] = { 0x9b, 0x30, 0x20, 0x71 };
                unsigned char c;

                SetMode(con, 1);
                Write(con, WSR, 4);

                do {
                    Read(con, &c, 1);
                } while (c != 0x9b);

                /* Ignora i primi tre numeri: */

                do {
                    Read(con, &c, 1);
                } while (c != 0x3b);
                do {
                    Read(con, &c, 1);
                } while (c != 0x3b);
                do {
                    Read(con, &c, 1);
                } while (c != 0x3b);

                /* Leggi l'ultimo numero */

                int w = 0;

                for (;;)
                {
                    Read(con, &c, 1);
                    if (c == 0x20)
                        break;
                    w = w*10 + (c - '0');
                }
                Read(con, &c, 1);   /* L'ultimo 0x72 */


                SetMode(con, 0);
                ShellSize = w - 1;

                D(Printf("ShellSize=%ld\n", w));

            }
        }


        /* Apriamo le librerie che forse sono utili */

        if (Opt(x))
            if ((XpkBase = OpenLibrary(XPKNAME, 0)) == NULL)
                UnsetOpt(x);


D(Printf("dirtolist ($%lx) = \"%s\" (%ld chars)\n", dirtolist, dirtolist, strlen(dirtolist)));


        /* Possiamo allocare la memoria */

        if (!errore && (pool = CreatePool( 0,
                sizeof(struct MinFib)*16, sizeof(struct MinFib)*16)))
        {
            /* Convertiamo il filtro. */

            unsigned char pattern[DIM_PATTERN];
            if (ParsePatternNoCase((STRPTR) argv[OPTPAT], pattern,
                    DIM_PATTERN) < 0)
                errore = IoErr();


            /* Prepariamoci ai MatchFirst()/MatchNext(). */

            struct AnchorPath *myextap = AllocMem(
                sizeof(struct AnchorPath) + PATHLEN,
                MEMF_PUBLIC);

            struct TagItem mytaglist[] =
            {
                XPK_InName, (LONG) myextap->ap_Buf,
                TAG_DONE,   0
            };


            /* __aligned struct AnchorPath myap;  */

            myextap->ap_Base= NULL;
            myextap->ap_Last = NULL;
            myextap->ap_Flags = 0;
            myextap->ap_BreakBits = SIGBREAKF_CTRL_C;
            myextap->ap_FoundBreak = 0;
            myextap->ap_Strlen = PATHLEN;

            errore = MatchFirst(dirtolist, myextap);

            if ((errore == 0) && ((myextap->ap_Flags & APF_ITSWILD) == 0))
            {
                /* L'argomento non contiene wildcard. */

                /* Se è una dir (e non abbiamo il flag ITSELF) entriamoci */
                /* e leggiamo il primo file. */
                /* Se è un file (o una dir, ma abbiamo il flag ITSELF) */
                /* allora mostriamolo, indipendentemente da PATTERN */

                if ((myextap->ap_Info.fib_DirEntryType > 0) && !Opt(i))
                {
                    myextap->ap_Flags |= APF_DODIR;
                    errore = MatchNext(myextap);
                }
                else
                    SetOpt(a);

            }

            /* Inizializziamo le liste che poi ordineremo. */

            struct MinFib *TestaDir = NULL;
            struct MinFib *TestaFile = NULL;
            struct MinFib *p;   // General-purpouse poniter.
            int DirCount = 0;
            int FileCount = 0;
            int maxl = 0;

            while (errore == 0)
            {
                /*
                *  Se non è la dir che abbiamo appena visitato, non è una
                *  dir quando vogliamo solo i file, non è un file quando
                *  vogliamo solo le dir e soddisfa il pattern,
                *  allora esaminiamo quest'entry!
                */
                int type = myextap->ap_Info.fib_DirEntryType;
                if (!(myextap->ap_Flags & APF_DIDDIR) &&
                    !((type > 0) && Opt(f)) &&
                    !((type < 0) && Opt(d)) &&
                    (Opt(a) || Match(myextap->ap_Info.fib_FileName)))
                {
                    if ((p=AllocPooled(pool, sizeof(struct MinFib))) == NULL)
                    {
                        errore = EE_MEM;
                        break;
                    }

                    CopyMemQuick(&myextap->ap_Info.fib_FileName, &p->Name, 32);
                    CopyMemQuick(&myextap->ap_Info.fib_Date, &p->u.Date,
                        sizeof(struct DateStamp));
                    p->Prot = myextap->ap_Info.fib_Protection;
                    p->Size = myextap->ap_Info.fib_Size;

                    switch (type)
                    {
                      case 1: case -1:  p->Type = 1; break;
                      case 2: case 10:  p->Type = 2; break;
                      case 3:           p->Type = 3; break;
                      case 4:           p->Type = 4; break;
                      case 5: case -5:  p->Type = 5; break;
                      default:          p->Type = 0; break;
                    }

                    if (type > 0)
                    {
                        /* DIR */
                        p->Next = TestaDir;
                        TestaDir = p;
                        ++DirCount;
                    } else
                    {
                        /* FILE */
                        p->Next = TestaFile;
                        TestaFile = p;
                        ++FileCount;

                        if (Opt(x))
                        {
                            struct XpkFib xpkfib;

                            XpkExamine(&xpkfib, mytaglist);

                            if (xpkfib.xf_Type > 0)
                            {
                                p->Type = 6;
                                p->u.Xpk.ULen = xpkfib.xf_ULen;
                                p->u.Xpk.Pack = *((ULONG *) xpkfib.xf_Packer);
                                p->u.Xpk.Padd = 0;
                                p->u.Xpk.Flag = ((xpkfib.xf_Flags & XPKFLAGS_PASSWORD)?
                                    '*\0' : 0);
                            }

                        }


                    }


                    if (!Opt(l))
                    {
                        int l = strlen(p->Name);
                        if (l > maxl)
                            maxl = l;
                    }

                }
                errore = MatchNext(myextap);
            }
            MatchEnd(myextap);

            /*
            *  Siamo usciti per un errore o per fine dei file?
            */
            if (errore == ERROR_NO_MORE_ENTRIES)
            {
                errore = 0;

                /*
                *  Gestione della lista delle dir.
                */
                if (DirCount)
                    errore = mostra2(DirCount, TestaDir, maxl, Opt(w));

                /*
                *  Gestione della lista dei file.
                */
                if ((!errore) && (FileCount))
                    errore = mostra2(FileCount, TestaFile, maxl, 0);
            }

            /*
            *  Deallochiamo la memoria.
            */

            DeletePool(pool);

        }
        else
            if (!errore)    errore = EE_MEM;

        FreeArgs(rda);

    } else errore = IoErr();

    /*
     *  Usciamo...
     */

    if (XpkBase)
        CloseLibrary(XpkBase);

/*
    if (DataTypesBase)
        CloseLibrary(DataTypesBase);
*/

    switch (errore)
    {
        case 0:
            _exit(RETURN_OK);
        case EE_BRK:
            PrintFault(304, NULL);
            _exit(RETURN_WARN);
        case EE_MEM:
            Printf("Memoria insufficiente.\n");
            _exit(RETURN_FAIL);
        default:
            PrintFault(errore, NULL);
            _exit(RETURN_FAIL);
    }

}



/*
**|ŻŻŻŻŻŻŻŻŻŻŻŻŻ|
**|  mostra2()  |
**|_____________|
*/
/*
 *  int count   = # di entry
 *  *testa      = ptr alla 1Ş entry
 *  int quick   = (max-len-filename / 4)+1 ; 0 per output lungo
 *  bool dir    = stampare in WHT?
 */

int mostra2(int count, struct MinFib *testa, int quick, int dir)
{
    unsigned char data[10];
    unsigned char ora[9];
    struct DateTime dt;

    dt.dat_Format   = FORMAT_DOS;
    dt.dat_Flags    = 0;
    dt.dat_StrDay   = NULL;
    dt.dat_StrDate  = data;
    dt.dat_StrTime  = ora;

    struct MinFib *p;
    struct MinFib **v;
    if (v=AllocPooled(pool, count * sizeof(void *)))
    {
        int i;
        int errore = 0; // (per il break)
        for (i=0,p=testa ; p ; p=p->Next)    v[i++] = p;
        HSort(v, count);

        if (dir)
            Printf(ANSI_WHT);

        if (!Opt(l) && !Opt(x))
        {
            /* Formato corto! */

            int x = (ShellSize / (quick + 3));

            int y = 1 + (int) ((count - 1) / x); /* operazione di ceiling */

            for (i=0 ; i < y ; ++i)
            {
                int n = i;
                int j;

                for (j=0 ; j<x ; ++j)
                {
                    if (n >= count) break;

                    if (j)
                        Printf("  ");

                    Printf("%s%lc", v[n]->Name, Desinenze[v[n]->Type] );

                    /* Printf("%s%s", v[n]->Name,  */
                    /* Desinenze[trail ? v[n]->Type : 0 ] );  */


                    Printf("%s", &Spazi[39 - quick + strlen(v[n]->Name)]);
                    n += y;

                    /* Break? */
                    if (CheckSignal(SIGBREAKF_CTRL_C))
                    {
                        errore = EE_BRK;
                        break;
                    }

                }

                Printf("\n");
            }


        } else
            /* Formato lungo! */

            for (i=0 ; i<count ; ++i)
            {
                unsigned char prot[] = "hsparwed";
                unsigned char *p = prot;
                unsigned int mask;

                /* Protezione */

                for (mask = 128 ; mask>8 ; ++p,mask>>=1)
                    if (!(v[i]->Prot & mask))
                        *p = '-';

                for ( ; mask>0 ; ++p,mask>>=1)
                    if (v[i]->Prot & mask)
                        *p = '-';

                /* Data e ora */
                dt.dat_Stamp = v[i]->u.Date;
                DateToStr(&dt);

                if (v[i]->Type == 0)
                {
                    Printf(FormatoFile, v[i]->Name, v[i]->Size, prot, data, ora);
                }
                else if (v[i]->Type == 6)
                {
                    Printf(FormatoXpk, v[i]->Name, v[i]->Size, prot,
                        v[i]->u.Xpk.ULen, & v[i]->u.Xpk.Pack);
                }
                else
                {
                    Printf(FormatoAltro, v[i]->Name,
                        Tipi[v[i]->Type], prot, data, ora);
                }


                Printf("%lc\n", Desinenze[v[i]->Type] );

                /* Printf("%s\n", Desinenze[ trail ? v[i]->Type : 0 ]);  */

                /* Break? */
                if (CheckSignal(SIGBREAKF_CTRL_C))
                {
                    errore = EE_BRK;
                    break;
                }

            }

        if (dir)
            Printf(ANSI_NORM);

        return errore;
    }
    else return EE_MEM;
}



/*
**|ŻŻŻŻŻŻŻŻŻŻŻ|
**|  HSort()  |
**|___________|
*/

#define Padre(x)    (((x)-1)>>1)
#define Sinistro(x) (((x)<<1)+1)
#define Destro(x)   (((x)+1)<<1)
#define Swap(a,b)   {void *t=a; a=b; b=t;}
/*#define Swap(a,b)   swap((int)a,(int)b)
**#define swap(a,b)   {a=(a)^(b);b=(a)^(b);a=(a)^(b);}
*/

void HSort(struct MinFib *v[], int dim)
{
    BuildHeap(v, dim);
    while (--dim > 0)
    {
        Swap(v[0], v[dim]);
        Heapfy(v, dim, 0);
    }
}

void Heapfy(struct MinFib *v[], int dim, int nodo)
{
    int maggiore = nodo;
    int i = Sinistro(nodo);
    if ((i < dim) && (Stricmp(v[i]->Name, v[maggiore]->Name) > 0))
        maggiore = i;
    i = Destro(nodo);
    if ((i < dim) && (Stricmp(v[i]->Name, v[maggiore]->Name) > 0))
        maggiore = i;
    if (maggiore != nodo)
    {
        Swap(v[nodo], v[maggiore]);
        Heapfy(v, dim, maggiore);
    }
}

void BuildHeap(struct MinFib *v[], int dim)
{
    int puf = Padre(dim-1);
    while (puf >= 0)
        Heapfy(v, dim, puf--);
}



