/*
	files.C

	DOS Files Tool v1.0

	Copyright (C) 1993, Geoff Friesen B.Sc.
	All rights reserved.

	Borland C++ 3.1
*/

#if !defined(__LARGE__)
#error Large Memory Model Expected
#endif

#include <dir.H>
#include <dos.H>
#include <process.H>
#include <stdio.H>
#include <stdlib.H>
#include <string.H>

#include "ceh.H"
#include "classcvt.H"
#include "dialog.H"
#include "edit.H"
#include "fd.H"
#include "kbd.H"
#include "macros.H"
#include "msg.H"
#include "str.H"
#include "video.H"

/* ------------------------------- */
/* Listbox Variables and Constants */
/* ------------------------------- */

int rows = 25;

#define	NENTRIES	(rows-9)
#define	ENTWIDTH	(DESIZE-1)

#define	NCOLS		(ENTWIDTH+2)
#define	NROWS		(NENTRIES+6)

int column, row;	/* upper-left listbox corner */

int di;			/* display index */
int mi;			/* master index */
int total;		/* total number of files */
int nentries;		/* number of displayable files */

#define	MAXFILES	250

struct ffblk delist [MAXFILES];

/* ---------------------- */
/* Dialog Box Identifiers */
/* ---------------------- */

#define	D_TOO_MANY_FILES	0
#define	D_NO_FILES		1
#define	D_CANNOT_DELETE		2
#define	D_ARE_YOU_SURE		3
#define	D_DIR_NOT_CHANGED	4
#define	D_FILE_NOT_RENAMED	5

/* ----------------- */
/* Screen Attributes */
/* ----------------- */

#define	NATTR	6

int attr [NATTR];

int bw [] =
{
   WHITE,				/* border */
   LIGHTGRAY,				/* normal file name */
   LIGHTGRAY << 4,			/* highlighted file name */
   LIGHTGRAY,				/* dialog/data entry title */
   LIGHTGRAY,				/* button name */
   WHITE				/* button hotkey */
};

int color [] =
{
   WHITE,				/* border */
   CYAN,				/* normal file name */
   (BLUE << 4) | WHITE,			/* highlighted file name */
   GREEN,				/* dialog/data entry title */
   LIGHTGRAY,				/* button name */
   YELLOW				/* button hotkey */
};

/* ------------------ */
/* Function Protoypes */
/* ------------------ */

int  	change_dir 	(void);
int  	dialog_box	(int type);
void 	down		(void);
int  	fetch		(char *path);
void 	highlight	(int mi, int attr, int col, int row);
char *	input		(char *title);
void 	refresh		(void);
void	rename_file	(void);
void 	sort		(void);
void 	up		(void);
void	view		(char *filespec);

void main (int argc, char **argv)
{
   int done = 0, i, vmode;

   if (_osmajor < 3 || _osmajor == 3 && _osminor < 30)
   {
       fprintf (stderr, "files: invalid DOS version number "
		"- needs 3.30+\n");
       exit (1);
   }

   if (argc > 2 ||
       argc == 2 && (rows = atoi (argv [1])) != 43 && rows != 50)
   {
       fprintf (stderr, "files: invalid command line\n");
       exit (1);
   }

   vmode = v_setup (rows);
   rows = v_getrows ();

   column = center(80, NCOLS);
   row = center(rows, NROWS);

   for (i = 0; i < NATTR; i++)
	attr [i] = (vmode == C80) ? color [i] : bw [i];

   v_cursor (CHIDE);

   _installceh ();

   if (fetch ("*.*") == -1)
   {
       v_cleanup ();
       exit (1);
   }

   v_setattr (attr [1]);

   v_paint (' ', column+1, row+1, NCOLS-2, NROWS-2);

   v_gotoxy (column+1, row+1);
   v_cputs ("FILES TOOL v1.0: ");

   v_gotoxy (column+2, row+NROWS-2);
   v_cputs ("RET-CD  F2-DEL F3-SORT F4-NAME F10-EXIT");

   v_setattr (attr [0]);

   v_border (DOUBLE_LINE, column, row, NCOLS, NROWS);

   v_gotoxy (column, row+2);
   v_putch ('Ì', 1);
   v_putch ('Í', ENTWIDTH);
   v_putch ('¹', 1);

   v_gotoxy (column, row+NROWS-3);
   v_putch ('Ì', 1);
   v_putch ('Í', ENTWIDTH);
   v_putch ('¹', 1);

   refresh ();	/* Display Files */

   do
   {
      switch (k_fetch ())
      {
	 case RET  : if (delist [mi].ff_attrib & FA_DIREC)
			 if (chdir (delist [mi].ff_name) == -1)
			     (void) dialog_box (D_DIR_NOT_CHANGED);
			 else
			     if (fetch ("*.*") != -1)
				 refresh ();
			     else
			     {
				 done = 1;
				 break;
			     }
		     break;

	 case F2   : if (dialog_box (D_ARE_YOU_SURE) != 'Y')
			 break;

		     if (unlink (delist [mi].ff_name) == -1)
		     {
			 (void) dialog_box (D_CANNOT_DELETE);
			 break;
		     }

		     if (mi != total-1)
			 for (i = mi+1; i < total; i++)
			      memcpy (&delist [i-1], &delist [i],
				      sizeof (struct ffblk));

		     if (!--total && change_dir () == -1)
		     {
			 done = 1;
			 break;
		     }

		     nentries = min(total, NENTRIES);
		     refresh ();
		     break;

	 case F3   : sort ();
		     refresh ();
		     break;

	 case F4   : rename_file ();
		     break;

	 case F10  : done = 1;
		     break;

	 case UP   : up ();
		     break;

	 case PGUP : for (i = 1; i <= nentries; i++)
			  up ();
		     break;

	 case HOME : if (!mi)
			 break;

		     highlight (0, attr [2], column+1, row+3);

                     for (mi = 1, di = 1; di < nentries; mi++, di++)
			  highlight (mi, attr [1], column+1, row+3+di);

		     mi = di = 0;
                     break;

	 case DOWN : down ();
		     break;

	 case PGDN : for (i = 1; i <= nentries; i++)
			  down ();
                     break;

	 case END  : if (mi == total-1)
			 break;

                     for (mi = total-nentries, di = 0; di < nentries-1;
			  mi++, di++)
			  highlight (mi, attr [1], column+1, row+3+di);

                     highlight (mi, attr [2], column+1, row+3+di);
      }
   }
   while (!done);

   v_cleanup ();
   exit (0);
}

int change_dir (void)
{
   char *buffer;
   int result = -2;

   if ((buffer = input (" Change Directory ")) != NULL)
       if (chdir (buffer) == -1)
	   (void) dialog_box (D_DIR_NOT_CHANGED);
       else
	   if ((result = fetch ("*.*")) != -1)
	       refresh ();

   return result;
}

int dialog_box (int type)
{
   DIALOG d;
   int hotkeys [2], result;
   char *titles [2], *choices [2];

   char *messages [] =
   {
      "There are too many files!",
      "There are no files!",
      "Unable to delete file!",
      "This file will be deleted!",
      "Are you sure?",
      "Unable to change directory!",
      "Unable to rename file!",
   };

   switch (type)
   {
      case D_TOO_MANY_FILES:

	   d.ntitles = 1;
	   titles [0] = messages [0];
	   d.nchoices = 1;
	   choices [0] = "Continue";
	   hotkeys [0] = 0;
	   break;

      case D_NO_FILES:

	   d.ntitles = 1;
	   titles [0] = messages [1];
	   d.nchoices = 1;
	   choices [0] = "Continue";
	   hotkeys [0] = 0;
	   break;

      case D_CANNOT_DELETE:

	   d.ntitles = 1;
	   titles [0] = messages [2];
	   d.nchoices = 1;
	   choices [0] = "Continue";
	   hotkeys [0] = 0;
	   break;

      case D_ARE_YOU_SURE:

	   d.ntitles = 2;
	   titles [0] = messages [3];
	   titles [1] = messages [4];
	   d.nchoices = 2;
	   choices [0] = "Yes";
	   choices [1] = "No";
	   hotkeys [0] = 0;
	   hotkeys [1] = 0;
	   break;

      case D_DIR_NOT_CHANGED:

	   d.ntitles = 1;
	   titles [0] = messages [5];
	   d.nchoices = 1;
	   choices [0] = "Continue";
	   hotkeys [0] = 0;
	   break;

      case D_FILE_NOT_RENAMED:

	   d.ntitles = 1;
	   titles [0] = messages [6];
	   d.nchoices = 1;
	   choices [0] = "Continue";
	   hotkeys [0] = 0;
   }

   d.titles = titles;
   d.choices = choices;
   d.hotkeys = hotkeys;

   d.tlattr = attr [3];
   d.chattr = attr [4];
   d.bdattr = *attr;
   d.hkattr = attr [5];

   if ((result = dialog (CENTER, CENTER, &d)) == -1)
   {
       (void) msg (MSG_ANY, titles [0], attr [0], attr [1]);
       (void) msg (MSG_ANY, "Insufficient memory for dialog box!",
		   attr [0], attr [1]);
       result = NUL;
   }

   return result;
}

void down (void)
{
   if (mi == total-1)
       return;

   highlight (mi++, attr [1], column+1, row+3+di);

   if (di < nentries-1)
       di++;
   else
       v_scroll (column+1, row+3, ENTWIDTH, nentries, SCROLL_UP, 1,
		 attr [1]);

   highlight (mi, attr [2], column+1, row+3+di);
}

int fetch (char *path)
{
   int done;
   struct ffblk de;

   total = 0;

   done = findfirst (path, &de, FA_DIREC | FA_HIDDEN | FA_SYSTEM);

   while (!done)
   {
      memcpy (&delist [total++], &de, sizeof(struct ffblk));
      done = findnext (&de);
      if (total == MAXFILES && !done)
      {
	  total = -1;
	  break;
      }
   }

   if (total == -1)
   {
       (void) dialog_box (D_TOO_MANY_FILES);
       return -1;
   }

   if (!total)
   {
       (void) dialog_box (D_NO_FILES);
       return -1;
   }

   nentries = min(total, NENTRIES);
   return 0;
}

void highlight (int index, int attr, int col, int row)
{
   char buffer [DESIZE];

   fde (delist+index, buffer);
   v_setattr (attr);
   v_gotoxy (col, row);
   v_cputs (buffer);
}

char *input (char *title)
{
   int result, x, y;
   static char buffer [MAXDIR+1], screen_buffer [(MAXDIR+2)*6];

   *buffer = '\0';

   x = center(80, MAXDIR+2);
   y = center(rows, 3);

   v_screen (SCREEN_SAVE, x, y, MAXDIR+2, 3, screen_buffer);

   v_setattr (attr [0]);
   v_border (DOUBLE_LINE, x, y, MAXDIR+2, 3);
   v_setattr (LIGHTGRAY);
   v_paint (' ', x+1, y+1, MAXDIR, 1);

   v_gotoxy (x+2, y);
   v_setattr (attr [3]);
   v_cputs (title);

   v_gotoxy (x+1, y+1);
   v_setattr (attr [2]);

   result = _editc (buffer, MAXDIR, EDIT_FETCH);

   v_screen (SCREEN_RESTORE, x, y, MAXDIR+2, 3, screen_buffer);

   return (result == CR) ? strtrm (buffer) : NULL;
}

void refresh (void)
{
   char buffer [10];

   highlight (0, attr [2], column+1, row+3);

   for (mi = 1, di = 1; di < nentries; mi++, di++)
	highlight (mi, attr [1], column+1, row+3+di);

   v_setattr (attr [1]);

   for (; di < NENTRIES; di++)
   {
	v_gotoxy (column+1, row+3+di);
	v_putch (' ', DESIZE-1);
   }

   mi = di = 0;

   ltoa (total, buffer, 10);
   strcat (buffer, " Files");
   v_gotoxy (column+18, row+1);
   v_setattr (attr [1]);
   v_cputs (buffer);
   v_cputs ("  ");
}

void rename_file (void)
{
   char *newname;

   if ((newname = input (" Rename File ")) != NULL)
       if (rename (delist [mi].ff_name, newname) == -1)
	   (void) dialog_box (D_FILE_NOT_RENAMED);
       else
	   if ((fetch ("*.*")) != -1)
	       refresh ();
}

void sort (void)
{
   unsigned i, pass;

   for (pass = 1; pass < total; pass++)
	for (i = 1; i <= total-pass; i++)
	     if (stricmp (delist [i-1].ff_name, delist [i].ff_name) > 0)
		 memswap (delist+i-1, delist+i, sizeof (struct ffblk));
}

void up (void)
{
   if (!mi)
       return;

   highlight (mi--, attr [1], column+1, row+3+di);

   if (di)
       di--;
   else
       v_scroll (column+1, row+3, ENTWIDTH, nentries, SCROLL_DN, 1,
		 attr [1]);

   highlight (mi, attr [2], column+1, row+3+di);
}