/* D  :  Directory replacement by Gregg Reno 5/28/87

  Format:   D  [spec] [option] [option] [option] [option]
   where
      spec   = file or directory spec.  Wild cards accepted (Ex: #?.c)
      option = one of the following:
                  -d,-D    list directories only
                  -f,-F    list files only
                  -n,-N    list files with no file extensions only
                  -l,-L    long format including #bytes, #blocks, protection,
                           timestamp and comments
                  -s,-S    Sort entries

   If no option is specified, entries are entered into three columns.
*/
#include "stdio.h"
#include "error.h"
#include "dos.h"

#define lf     '\n'
#define esc    27
#define ff     12
#define space  ' '
#define DTYPE  1
#define FTYPE  2
#define MAXFILES 100     /* Change this if you have more files */

typedef struct JULDATE {
    short  year;
    short  days;
    };

short days_before[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};

long int lseconds,nfiles=0,nbytes=0,nblocks=0,curpos,ndirs=0;
char  *files[MAXFILES],*curfile;
char type[MAXFILES];
char  *month_str[12] = { "Jan ","Feb ","Mar ","Apr ","May ","Jun ","Jul ",
                         "Aug ","Sep ","Oct ","Nov ","Dec "};

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

   int   error,len,temp;
   struct FILEINFO info;
   struct JULDATE jdate;
   char  tstring[80],tstring2[80], column, i, opt,
         prot[7], files_only, dir_only,
         lformat, sort, wildc, noex;

   /* Default settings */
   files_only=FALSE;                /* Files and directories */
   dir_only = FALSE;
   lformat = FALSE;                 /* No long format */
   noex = FALSE;                    /* No file extension check */
   sort=FALSE;                      /* No sorting */
   wildc = FALSE;                   /* No wildcards found in string */
   scpy(tstring2, "#?");            /* Default is wildcard */

   for (i=1; i < argc; ++i) {
      if (argv[i][0] == '-') {            /* Option selected */
         opt = argv[i][1];
         if (opt >= 'a' && opt <= 'z') opt = opt - 'a' + 'A';  /* Upper case */
         switch (opt) {
            case 'D':   dir_only = TRUE;
                        break;
            case 'F':   files_only = TRUE;
                        break;
            case 'L':   lformat = TRUE;
                        break;
            case 'S':   sort = TRUE;
                        break;
            case 'N':   noex = TRUE;
                        break;
            default:    ps("  ** Invalid option: -",22);
                        pc(argv[i][1]);
                        pc(lf);
                        exit();
            }
         }
      else
         scpy(tstring2, argv[i]);
      }

   if (lformat) {
      pc(lf);
      ps("  File Name                Bytes   Blocks  Prot  Time Stamp",70);
      pc(lf);
      ps("  ------------------------ ------- ------  ----  --------------------",70);
      pc(lf);
      }
   len = strlen(tstring2);
   curpos = 0;
   for (i=0; i < len; ++i) {
      if (tstring2[i] == '.' && (i==0 || i==(len-1))) { /* Dot at beg or end */
            if (i==(len-1)) tstring[curpos++] = '.';
            tstring[curpos++] = '#';
            tstring[curpos++] = '?';
            if (i==0) tstring[curpos++] = '.';
            wildc = TRUE;
            }
      else if (tstring2[i] == ':' && i==len-1) {         /* Colon at end */
            tstring[curpos++] = ':';
            tstring[curpos++] = '#';
            tstring[curpos++] = '?';
            wildc = TRUE;
            }
      else if (tstring2[i] == '#' || tstring2[i] == '?') {
            wildc = TRUE;
            tstring[curpos++] = tstring2[i];
            }
      else if (tstring2[i] == '*') {
         tstring[curpos++] = '#';
         tstring[curpos++] = '?';
         wildc = TRUE;
         }
      else
         tstring[curpos++] = tstring2[i];
      }
   tstring[curpos] = '\0';
   len = curpos;

   error = dfind(&info, tstring, 1);

   /* Check to see if they specified a directory */
   if (error==0 && info.fib_DirEntryType>0 && len >0 && wildc == FALSE) {
      scpy(&tstring[len], "/#?");
      error = dfind(&info, tstring, 1);
      }

   while (error==0) {
      if (noex)                          /* Search for file extension */
         for (i=0; info.fib_FileName[i] != 0; ++i)
            if (info.fib_FileName[i]=='.') goto next;

      if (files[nfiles]==0) {             /* Get memory */
         if (lformat) files[nfiles] = calloc(1,190);
         else files[nfiles] = calloc(1,35);
         }
      curfile = files[nfiles];
      curpos = 0;

      /* Entry Name */
      if (info.fib_DirEntryType > 0) {     /* Directory entry */
         if (files_only == TRUE) goto next;
         type[nfiles] = DTYPE;
         pmc(ff);
         pms(info.fib_FileName,25);
         ++ndirs;                         /* Number of directories */
         }
      else {                               /* File entry */
         if (dir_only == TRUE) goto next;
         type[nfiles] = FTYPE;
         pms(info.fib_FileName,25);
         }

      if (lformat) {                     /* Long format */
         if (type[nfiles] == DTYPE)
            pms(" ",16);
         else {
            nbytes += info.fib_Size;
            stcl_d(tstring, info.fib_Size);        /* # bytes */
            pms(tstring,8);
            nblocks += info.fib_NumBlocks;
            stcl_d(tstring, info.fib_NumBlocks);   /* # blocks */
            pms(tstring,8);
            }
         scpy(prot, "----  ");                  /* protection */
         if ((info.fib_Protection & 8) == 0) prot[0] = 'r';
         if ((info.fib_Protection & 4) == 0) prot[1] = 'w';
         if ((info.fib_Protection & 2) == 0) prot[2] = 'e';
         if ((info.fib_Protection & 1) == 0) prot[3] = 'd';
         pms(prot,6);
         Get_Date(&jdate, &info.fib_Date);
         JultoGreg(&jdate);
         pms("  ",2);
         temp = info.fib_Date.ds_Minute/60;              /* Hour */
         if (temp<10) pmc('0');
         stcl_d(tstring, temp);   /* # blocks */
         pms(tstring,0);
         temp = info.fib_Date.ds_Minute - (temp * 60);   /* Minute */
         pmc(':');
         if (temp<10) pmc('0');
         stcl_d(tstring, temp);
         pms(tstring,0);
         pmc(lf);
         if (info.fib_Comment[0] != '\0') {
            pms(" ",5);
            pms(info.fib_Comment,70);
            pmc(lf);
            }
         }
      ++nfiles;
next:
      error = dnext(&info);
      }
   if (sort)
      strsrt(files, nfiles);
   column = 1;
   for (i=0; i<nfiles; ++i) {
      if (column==1) ps("  ",2);
      if (sort== TRUE && i < ndirs){   /* If we are sorting , all directory */
         pc(esc);                      /* files are at the beginning */
         ps("[0;33;40m",9);
         ps(files[i],0);
         pc(esc);
         ps("[0;31;40m",9);
         }
      else if (sort == FALSE && type[i]==DTYPE) {
         pc(esc);
         ps("[0;33;40m",9);
         ps(files[i],0);
         pc(esc);
         ps("[0;31;40m",9);
         }
      else
         ps(files[i],0);
      free(files[i]);
      if (lformat==FALSE) {
         ++column;
         if (column>3) {
            pc(lf);
            column=1;
            }
         }
      }
   if (lformat) {
      ps("  ------------------------ ------- ------",0);
      pc(lf);
      }
   else if (column > 1) pc(lf);
   stcl_d(tstring, nfiles);   /* # files */
   ps("  ",2);
   ps(tstring,3);
   ps(" files",22);
   if (lformat) {
      stcl_d(tstring, nbytes);   /* # bytes */
      ps(tstring,8);
      stcl_d(tstring, nblocks);  /* # blocks */
      ps(tstring,8);
      }
   pc(lf);
   }

ps(string,len)          /* Replace printf to shrink program */
   char  string[];
   {
   int   i,j;
   i=0;
   while (string[i] != '\0') {
      if (string[i] != ff) putchar(string[i]);
      ++i;
      }
   if (len != 0) for (j=i; j<len; ++j) putchar(space);
   }

pc(ch)
   char  ch;
   {
   putchar(ch);
   }

pms(string,len)          /* put string in memory */
   char  string[];
   {
   int   i,j;
   i=0;
   while (string[i] != '\0') {
      curfile[curpos] = string[i];
      ++i;
      ++curpos;
      }
   for (j=i; j<len; ++j, ++curpos) curfile[curpos] = space;
   }

pmc(ch)                 /* Put character in memory */
   char  ch;
   {
   curfile[curpos] = ch;
   ++curpos;
   }

scpy(to_string,from_string)            /* Replace strcpy to shrink program */
   char to_string[],from_string[];
   {
   int   i;
   for (i=0; from_string[i] != '\0'; ++i)
      to_string[i] = from_string[i];
   to_string[i] = '\0';
   }

/*
    The following routine accepts one argument, a pointer to a JULDATE
    structure.  It returns the current date in Julian date form.
*/
Get_Date(date,datestmp)
struct JULDATE *date;
long datestmp[3];
{
    int year_span, years_left, remainder, leapdays;

    datestmp[0] += 1;                  /*  add 1 for Jan. 1, 1978  */
    year_span = datestmp[0] / 366;     /*  approx. year  */
    date->days = datestmp[0] % 366;    /*  # of days left over  */
    leapdays = year_span / 4;          /*  approx. # of leap days  */
    date->year = 1978 + year_span;     /*  current year  */
    years_left = year_span % 4;        /*  years since last leap yr  */
    remainder = date->year % 4;        /*  # of years since last leap  */
    if (remainder <= years_left && remainder > 0)
         leapdays += 1;      /*  recent year was a leap year  */
    date->days += year_span - leapdays;     /*  correct for leap days  */
    if (remainder > 0 && date->days > 365)
         {
         date->year += 1;
         date->days -= 365;
         }
    else if (remainder == 0 && date->days > 366)
         {
         date->year += 1;
         date->days -= 366;
         }

}

/*
    This function accepts two arguments:  a string pointer and a pointer to a
    JULDATE structure.  The routine will convert the Julian date to Gregorian
    (mm-dd-yyyy) format.  The string pointer must point to a space at least
    11 characters long.
*/
JultoGreg(date)
struct JULDATE *date;
{
    int length, year, month, day;
    short julday;
    char string[6];

    year = date->year;
    julday = date->days;
    if ( !(year % 4) && julday > 60)  julday -= 1;
         /*  subtract 1 if we are in a leap year & past Feb.  */
    for (month = 0; (days_before[month] < julday) && (month < 12); month++);
         /*  find month  */
    day = julday - days_before[month-1];    /*  get current day  */

    pms(month_str[month-1],4);
    length = stci_d(string, day, 6);
    if (day<10) pmc('0');
    pms(string,0);         /*  add day to greg_date  */
    pmc( ',');
    length = stci_d(string, year, 6);
    pms(string,0);                 /*  add year to greg_date  */
}
