
/* Search.c  routines for searching data and strings on disk */

#include <exec/memory.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>

struct SData
{
   UWORD FirstTrack, LastTrack;
   UBYTE SType;
   UBYTE ByteCount;
   UBYTE SearchData[40];
};

struct Requester *BlockInput();

extern struct Window *Window;
extern struct Gadget SchuifGadget;
extern struct PropInfo Schuif_info;
extern struct StringInfo Block_info, Track_info, Sector_info;
extern APTR My_VisualInfo;
extern struct TextAttr Style;
extern int drive;
extern UBYTE *BB;
extern UBYTE CursorX, CursorY, RealCursorY;

struct IOStdReq *OpenTDIO();

char *MethodsTxt[] =
{  "A = a", "A <> a", "Relative", NULL };

/* Search-modes */
#define sf_NCS    0
#define sf_Normal 1
#define sf_Rel    2
#define sf_Number 3
#define sf_total  4

/* Gadget Id's */
#define S_Method 11
#define S_Cancel 12
#define S_String 13
#define S_FTrack 14
#define S_LTrack 15
#define S_Start  16
#define S_Stop   21

UBYTE SIText[] = "Searching Track    ", FIText[] = "Track    ";
struct IntuiText SearchText = {  1, 0, JAM2, 0, 0, &Style, SIText, NULL  };

struct NewGadget
SFTrackNG = {  32, 44, 44, 13, "Start track", &Style, S_FTrack, PLACETEXT_BELOW,
   NULL, NULL },
SLTrackNG = {  136, 44, 44, 13, "End track", &Style, S_LTrack, PLACETEXT_BELOW,
   NULL, NULL },
SStringNG = {  12, 16, 180, 13, "String", &Style, S_String, PLACETEXT_BELOW,
   NULL, NULL },
SMethodNG = {  72, 76, 92, 12, "Case", &Style, S_Method, 0, NULL, NULL },
SCancelNG = {  124, 94, 60, 12, "Cancel", &Style, S_Cancel, 0, NULL, NULL },
SStartNG  = {  20, 94, 60, 12, "Start", &Style, S_Start, 0, NULL, NULL },
SStopNG   = {  46, 52, 108, 12, "Abort Search", &Style, S_Stop, 0, NULL, NULL };

void DoStringGad(gad, window, data)
struct Gadget *gad;
struct Window *window;
struct SData *data;
{
   struct StringInfo *SI;
   int i;

   SI = (struct StringInfo *) gad->SpecialInfo;
   for (i = 0; SI->Buffer[i] != 0; i++)
      data->SearchData[i] = SI->Buffer[i];
   data->SearchData[i] = 0;
   data->ByteCount = i;
}

void DoFTrackGad(gad, window, data)
struct Gadget *gad;
struct Window *window;
struct SData *data;
{
   struct StringInfo *SI;
   UBYTE track;

   SI = (struct StringInfo *) gad->SpecialInfo;
   track = (UBYTE) SI->LongInt;
   if ((track < 0) || (track > data->LastTrack))
   {
      track = data->FirstTrack;
      sprintf(SI->Buffer, "%d\x00", data->FirstTrack);
      SI->LongInt = track;
      RefreshGList(gad, window, NULL, 1);
   }
   else data->FirstTrack = track;
}

void DoLTrackGad(gad, window, data)
struct Gadget *gad;
struct Window *window;
struct SData *data;
{
   struct StringInfo *SI;
   UBYTE track;

   SI = (struct StringInfo *) gad->SpecialInfo;
   track = (UBYTE) SI->LongInt;
   if ((track < 0) || (track > 159) ||
      (track < data->FirstTrack))
   {
      track = data->LastTrack;
      sprintf(SI->Buffer, "%d\x00", track);
      SI->LongInt = track;
      RefreshGList(gad, window, NULL, 1);
   }
   else data->LastTrack = track;
}

void OpenRequester(window, data, mode)
struct Window *window;
struct SData *data;
UBYTE mode;
{
   BOOL terminated = FALSE;
   struct Window *SWindow;
   struct IntuiMessage *imsg;
   struct Gadget *gad, *glist = NULL;
   struct Gadget *FTR_gad, *LTR_gad, *SS_gad;
   int i;
   UBYTE *title;

   data->ByteCount = 0;
   data->FirstTrack = 0;
   data->LastTrack = 159;
   data->SType = mode;

   SStartNG.ng_VisualInfo = My_VisualInfo;
   SCancelNG.ng_VisualInfo = My_VisualInfo;
   SMethodNG.ng_VisualInfo = My_VisualInfo;
   SStringNG.ng_VisualInfo = My_VisualInfo;
   SFTrackNG.ng_VisualInfo = My_VisualInfo;
   SLTrackNG.ng_VisualInfo = My_VisualInfo;

   gad = CreateContext(&glist);
   if (mode != sf_Number) gad = CreateGadget(CYCLE_KIND, gad, &SMethodNG,
      GTCY_Labels, MethodsTxt, GTCY_Active, mode, TAG_END);
   gad = CreateGadget(BUTTON_KIND, gad, &SStartNG, TAG_END);
   gad = CreateGadget(BUTTON_KIND, gad, &SCancelNG, TAG_END);
   SS_gad = gad = CreateGadget(STRING_KIND, gad, &SStringNG,
      GTST_String, NULL, GTST_MaxChars, 20L, TAG_END);
   gad->Activation |= GACT_STRINGCENTER;
   FTR_gad = gad = CreateGadget(INTEGER_KIND, gad, &SFTrackNG,
      GTIN_Number, 0L, GTIN_MaxChars, 3L, TAG_END);
   gad->Activation |= GACT_STRINGCENTER;
   LTR_gad = gad = CreateGadget(INTEGER_KIND, gad, &SLTrackNG,
      GTIN_Number, 159L, GTIN_MaxChars, 3L, TAG_END);
   gad->Activation |= GACT_STRINGCENTER;

   if (mode == sf_Number) title = "Search number";
   else title = "Search string";

   SWindow = OpenWindowTags(NULL,
      WA_Title,     title,
      WA_Gadgets,   glist,      WA_AutoAdjust,    TRUE,
      WA_Top,          40,      WA_Left,           180,
      WA_Width,       204,      WA_InnerHeight,    100,
      WA_DragBar,    TRUE,      WA_DepthGadget,   TRUE,
      WA_Activate,   TRUE,
      WA_IDCMP, CYCLEIDCMP | BUTTONIDCMP | STRINGIDCMP,
      TAG_END);

   if (SWindow)
   {
      ActivateGadget(SS_gad, SWindow, NULL);
      while (!terminated)
      {
         Wait (1 << SWindow->UserPort->mp_SigBit);
         while ((!terminated) && (imsg =  GT_GetIMsg(SWindow->UserPort)))
         {
            switch (imsg->Class)
            {
               case IDCMP_GADGETUP:
                  gad = (struct Gadget *)imsg->IAddress;
                  switch(gad->GadgetID)
                  {
                     case S_Start:
                        DoStringGad(SS_gad, SWindow, data);
                        DoFTrackGad(FTR_gad, SWindow, data);
                        DoLTrackGad(LTR_gad, SWindow, data);
                        terminated = TRUE;
                     break;

                     case S_Cancel:
                        data->ByteCount = 0;
                        terminated = TRUE;
                     break;

                     case S_Method:
                        data->SType = imsg->Code;
                     break;

                     case S_FTrack:
                        DoFTrackGad(gad, SWindow, data);
                     break;

                     case S_LTrack:
                        DoLTrackGad(gad, SWindow, data);
                     break;
                  }
               break;
            }
         GT_ReplyIMsg(imsg);
         }
      }
      CloseWindow(SWindow);
   }
   FreeGadgets(glist);
}

BOOL Digit(ch)
UBYTE ch;
{
   return((ch >= '0') && (ch <= '9'));
}

BOOL ABCDEF(ch)
UBYTE ch;
{
   return(((ch >= 'a') && (ch <= 'f')) || ((ch >= 'A') && (ch <= 'Z')));
}

BOOL HexDigit(ch)
UBYTE ch;
{
   return(Digit(ch) || ABCDEF(ch));
}

UBYTE HexVal(digit)
UBYTE digit;
{
   if (Digit(digit)) return(digit - '0');
   else return(10 + ((digit - 'a') & 0x0f));
}

UBYTE ComposeByte(dig1, dig2)
UBYTE dig1, dig2;
{
   UBYTE rb;

   return(16*HexVal(dig1) + HexVal(dig2));
}

int ProcessSString(string)
UBYTE *string;
{
   UBYTE dstring[40];
   int i, j, k;
   ULONG UL;
   BOOL ok = TRUE, error = FALSE;

   for (i = 0; string[i] != 0; i++) dstring[i] = string[i];
   dstring[i] = 0;
   i = 0;
   j = 0;
   while (ok && !error)
   {
      while (dstring[i] == ' ') i++;
      if (dstring[i] == '$')
      {
         i++;
         for (k = 0; HexDigit(dstring[i+k]); k++);
         if (k == 0) error = TRUE;
         else while (k > 0)
         {

            if (k % 2)
            {
               string[j] = ComposeByte('0', dstring[i++]);
               k--;
            }
            else
            {
               string[j] = ComposeByte(dstring[i], dstring[i+1]);
               i += 2;
               k -= 2;
            }
            j++;
         }
      }
      else if (Digit(dstring[i]))
      {
         for (k = 0; Digit(dstring[i+k]); k++);
         if (k > 10) error = TRUE;
         else
         {
            UL = 0;
            while (Digit(dstring[i])) UL = 10*UL + dstring[i++] - '0';
            if (UL < 256) string[j++] = (UBYTE) UL;
            else if (UL < 65536)
            {
               string[j++] = (UBYTE) (UL / 256);
               string[j++] = (UBYTE) (UL & 0xff);
            }
            else
            {
               string[j++] = (UBYTE) (UL / 16777216);
               string[j++] = (UBYTE) (UL / 65536);
               string[j++] = (UBYTE) (UL / 256);
               string[j++] = (UBYTE) (UL & 0xff);
            }
         }
      }
      else if (dstring[i] == '\'')
      {
         i++;
         while (dstring[i] != '\'') string[j++] = dstring[i++];
         i++;
      }
      else if (dstring[i] == 0) ok = FALSE;
      else error = TRUE;
   }
   string[j] = 0;
   if (error) j = 0;
   return(j);
}

void UpDateSText(rport, track)
struct RastPort *rport;
UBYTE track;
{
   sprintf(&SIText[16], "   ");
   sprintf(&SIText[16], "%d", track);
   PrintIText(rport, &SearchText, (200-IntuiTextLength(&SearchText))/2L, 20L);
}

BOOL Match(string1, string2, n)
UBYTE *string1, *string2;
int n;
{
   int i;
   BOOL rc;

   rc = TRUE;
   i = 0;
   while (rc && (i < n))
   {
      rc &= (string1[i] == string2[i]);
      i++;
   }
   return(rc);
}

UBYTE UpCase(ch)
UBYTE ch;
{
   if ((ch >= 'a') && (ch <= 'z')) return(ch - 'a' + 'A');
   else return(ch);
}

BOOL MatchNCS(string1, string2, n)
UBYTE *string1, *string2;
int n;
{
   int i;
   BOOL rc;

   rc = TRUE;
   i = 0;
   while (rc && (i < n))
   {
      rc &= (UpCase(string1[i]) == UpCase(string2[i]));
      i++;
   }
   return(rc);
}

BOOL MatchRel(string1, string2, n)
BYTE *string1, *string2;
int n;
{
   int i;
   BYTE os;
   BOOL rc;

   rc = TRUE;
   os = string1[0] - string2[0];
   i = 1;
   while ((i < n) && rc)
   {
      rc &= (string1[i] - string2[i] == os);
      i++;
   }
   return(rc);
}

ULONG Search(data, drive, type)
struct SData *data;
int drive;
{
   int j, n, m;
   ULONG SE, OS, ROS, LTR;
   BOOL eq, stop;
   UBYTE *s, *tdb, *virtbuf;
   struct Gadget *gad, *glist = NULL;
   struct Window *SBWindow;
   struct RastPort *SBRPort;
   struct IntuiMessage *imsg;
   BOOL (*MatchF[sf_total])();
   struct IOStdReq *SearchIO;

   tdb = (UBYTE *) AllocMem(2*512L, MEMF_CHIP);
   if (!tdb)
   {
      puts("Not enough memory!");
      return(0);
   }
   else if ((SearchIO = OpenTDIO(drive)))
   {
      MatchF[sf_Normal] = Match;
      MatchF[sf_NCS] = MatchNCS;
      MatchF[sf_Rel] = MatchRel;
      MatchF[sf_Number] = Match;

      SStopNG.ng_VisualInfo = My_VisualInfo;
      gad = CreateContext(&glist);
      gad = CreateGadget(BUTTON_KIND, gad, &SStopNG, TAG_END);
      SBWindow = OpenWindowTags(NULL, WA_Title,     "Searching...",
         WA_Gadgets,   glist,      WA_AutoAdjust,    TRUE,
         WA_Top,          40,      WA_Left,           180,
         WA_Width,       200,      WA_InnerHeight,     60,
         WA_DragBar,    TRUE,      WA_DepthGadget,   TRUE,
         WA_Activate,   TRUE,
         WA_IDCMP, BUTTONIDCMP,    TAG_END);
      SBRPort = SBWindow->RPort;

      n = data->ByteCount;
      s = data->SearchData;
      m = data->SType;
      SE = 11*512L*(data->LastTrack + 1);
      LTR = data->FirstTrack;
      OS = 11*512L*LTR;
      eq = FALSE;
      stop = FALSE;
      UpDateSText(SBRPort, (UBYTE) LTR);

      while ((OS < SE) && !eq && !stop)
      {
         ROS = OS & 0xfffffe00;
         if (ROS / (512*11) > LTR)
         {
            LTR = ROS / (512*11);
            UpDateSText(SBRPort, (UBYTE) LTR);
         }
         if (ReadSBlock(SearchIO, ROS / 512, 2, tdb)) puts("error");
         virtbuf = &tdb[OS - ROS];
         for (j = 0; (j < 512-n) && !eq; j++)
         {
            eq = (*MatchF[m])(&virtbuf[j], s, n);
         }
         if (!eq) OS += 512 - n;
         if (imsg =  GT_GetIMsg(SBWindow->UserPort))
         {
            GT_ReplyIMsg(imsg);
            stop = TRUE;
         }
      }
      MotorOff(SearchIO);
      CloseTDIO(SearchIO);
      FreeMem(tdb, 2*512L);
      CloseWindow(SBWindow);
      FreeGadgets(glist);

      if (eq) return((OS + j - 1) | 0x80000000);
      else return(0L);
   }
}

void ShowDataFound(offset, type)
ULONG offset;
UBYTE type;
{
   ULONG K;

   offset &= 0x7fffffff;
   ReadBlocks(drive, offset / 512, 1, BB);
   if (type == sf_Number) CursorX = (offset & 0x0000000f)*2;
   else CursorX = (offset & 0x0000000f) + 32;
   RealCursorY = (offset & 0x000001f0) / 16;
   if (RealCursorY < 16)
   {
      K = 0L;
      CursorY = RealCursorY;
   }
   else
   {
      K = 0xffff;
      CursorY = RealCursorY - 16;
   }
   NewModifyProp(&SchuifGadget, Window, NULL,
      Schuif_info.Flags, 0, (UWORD) K, 0,
      Schuif_info.VertBody, 1);
   UpDateSB();
   Track_info.LongInt = offset / (512*11);
   Sector_info.LongInt = (offset / 512) % 11;
   Block_info.LongInt = offset / (512);
   UpDateTrSeG();
   UpDateBlockG();
   sprintf(&SIText[16], "   ");
   sprintf(&FIText[6], "%d", (UBYTE) (offset / (512*11)));
   if (type == sf_Number) ShowText2(Window, "Data Found at", FIText, NULL);
   else ShowText2(Window, "String found at", FIText, NULL);
}

void SearchS()
{
   struct SData SSData;
   struct Requester *IB;
   ULONG L = 0, K;

   IB = BlockInput(Window);
   OpenRequester(Window, &SSData, sf_NCS);
   if (SSData.ByteCount > 0)
   {
      L = Search(&SSData, drive);
      if (L) ShowDataFound(L, sf_Normal);
      else ShowText2(Window, "String not found", "End of search", NULL);
   }
   UnBlockInput(IB, Window);
}

void SearchB()
{
   struct SData NSData;
   ULONG L = 0, K;
   struct Requester *IB;

   IB = BlockInput(Window);
   OpenRequester(Window, &NSData, sf_Number);
   if (NSData.ByteCount > 0)
   {
      if ((NSData.ByteCount = ProcessSString(NSData.SearchData)) > 0)
      {
         L = Search(&NSData, drive);
         if (L) ShowDataFound(L, sf_Number);
         else ShowText2(Window, "Data not found", "End of search", NULL);
      }
      else ShowText2(Window, "Error in search-input", NULL, NULL);
   }
   UnBlockInput(IB, Window);
}
