/*- - - - - - - - - - YAFR - Yet Another File Requester - - - - - - - - -*/
/*- - - - - - - - - - - - - - Version  1.21 - - - - - - - - - - - - - - -*/

/*YAFR V1.21 Copyright (c)1989,1990 by Dave Schreiber. All rights reserved.*/
/*YAFR is freely distributable, and may be sold as part of a commercial  */
/*program. This copyright message must be included in the documentation  */
/*for any program, commercial or otherwise, that uses YAFR. Use of YAFR  */
/*in a rogram does not detract in any way from the ownership of YAFR by  */
/*its author, Dave Schreiber, nor does it change its status as a freely  */
/*distributable program.  Dave Schreiber reserves the right to forbid any*/
/*person or company from incorporating YAFR as part of a program owned by*/
/*that person or company.                                                */

/*Compiled under Lattice C V5.04                                         */
/*Command line:  1> lc -cw -j30 yafr                                     */
/*Generates YAFR.o which is linked into the host program                 */

/*If you have any comments, suggestions, etc. I can be reached at:       */
/*Dave Schreiber                                                         */
/*1234 Collins Lane                                                      */
/*San Jose, CA   95129-4208                                              */
/*Usenet:  davids@ucscb.ucsc.edu (during the school year)                */
/*         davids@cup.portal.com (other times)                           */

#include "yafrHead.h"
             /*^^^^system includes, structure definitions, etc.*/
#define CRNT header.current->fib

/*Parameters are:  fn-string for filename (33 characters)                */
/*dn-string for directory name (150 characters)                          */
/*en-string for file pattern (#?.c, file1?, etc. 150 characters)         */
/*cn-string for combined directory and filename (183 characters)         */
/*(NULL if you don't want the combined filename)                         */
/*X,Y-YAFR's window's X,Y location (relative to the screen               */
/*wn-text for YAFR's window's title bar                                  */
/*scr-Pointer to screen that window should appear on (NULL for           */
/*Workbench) filepen-Pen color that filenames should be displayed in     */
/*dirpen-Pen color that directory names should be displayed in           */
/*DisableInfo-Boolean; TRUE-files ending in .info aren't displayed       */
/*                     FALSE-.info files are displayed                   */

/*Note!  IntuitionBase, GfxBase, and DOSBase must be opened by the       */
/*calling program before YAFR is called.  DOSBase is usually opened by   */
/*the C startup code, but the others aren't.  If they aren't, YAFR will  */
/*crash!  Be warned.                                                     */

/*Version 1.1, completed October 30, 1989*/
/*Directory and filename string gadgets now auto-activate and appropriate*/
/*times.  A bug that caused an incorrect filename to be returned when the*/
/*user pressed CANCEL has been fixed                                     */

/*Version 1.15 completed January 23, 1990*/
/*A bug that could cause the name of a directory to run outside the      */
/*filename box has been eliminated.  Also, device names are now converted*/
/*into volume names when selected from the device name buttons on the    */
/*right (ex. on my system, when DH2: is pressed, Data: is the name that  */
/*appears in the directory box.  The source code has had many more       */
/*comments added to it and there are new distribution requirements.      */

/*Version 1.21 completed May 6, 1990*/
/*The device gadgets now get their choices from the AmigaDOS list of     */
/*devices, instead of being hard-coded in.  Filenames can now be selected*/
/*by double clicking on the filename in the filename box.  The code has  */
/*also been cleaned up a little (Lattice & Amiga functions are now used  */
/*in place of some of the custom functions used in V1.15 & previous, in  */
/*particular, ispipedevice() and RefreshGadget() ).  Finally, the what   */
/*used to be the filename extension box has now become a more general--  */
/*purpose wildcard selector (using standard AmigaDOS wildcards; no *     */
/*here!).  Thanks to Dave Haynie for the DiskSalv wildcard code I used.  */

/*Thanks also to Bruce Rogers for his FindRoot code which has been       */
/*incorporated in YAFR.                                                  */

/*-----------------------------------------------------------------------*/

#include "pattern.h"

#ifdef DSOIFEW
typedef struct {        /*Required for pattern matching routine*/
   char *aux;
   BYTE type;
} pattern;

extern pattern *CompilePattern(char *str);
extern BOOL MatchPattern(pattern *,char *);
#endif

pattern *MasterPattern;

/*Main function.  This is what is called by the host program:*/

GetFilename(fn,dn,en,cn,X,Y,wn,scr,filepen,dirpen,DisableInfo)
char *fn,*dn,*en,*cn,*wn;  /*See above for parameter definitions*/
USHORT X,Y,filepen,dirpen;
BYTE DisableInfo;
struct Screen *scr;
{
   int status=GN_NEWDIR;
   struct FileLock *DirLock;
   char temp[150];

   if(DisableInfo)
      Info.Flags|=SELECTED;
   else
      Info.Flags=GADGHIMAGE|GADGIMAGE;

   yafr_e_FilenamePenColor = filepen;
   yafr_e_DirnamePenColor = dirpen;
   DirLock=(struct FileLock *)Lock(dn,ACCESS_READ);
         /*Get lock on first directory*/
   
   if(en[0]==NULL)   /*If no pattern was provided*/
      strcpy(en,"#?");  /*provide one that will give us everything*/
   
   strcpy(temp,en);
   
   MasterPattern = (pattern *)CompilePattern(temp);
      /*Compile the pattern matching structure*/
   
   strcpy(DirNameSIBuff,dn);  /*Copy some parameters to this modules*/
   strcpy(ExtNameSIBuff,en);  /*variables*/
   strcpy(filenameSIBuff,fn);
   NewFNWindow.LeftEdge=X;
   NewFNWindow.TopEdge=Y;
   NewFNWindow.Title=(char *)wn;
   NewFNWindow.Screen=(struct Screen *) scr;
   
         /*If there's a screen, open on it.  Workbench screen otherwise*/
   NewFNWindow.Type= (scr) ? CUSTOMSCREEN : WBENCHSCREEN;

   PutDevicesInGadgets(NULL,NULL);
      /*Open the window*/
   if((FNWindow=(struct Window *)OpenWindow(&NewFNWindow))==NULL)
      {  /*If it failed...*/
      UnLock(DirLock); /*Unlock the directory*/
      return(FALSE);   /*and return*/
      }
      
   /*Write some important text into the title bar:  */
   SetWindowTitles(FNWindow,-1,"YAFR V1.21 (c)1989, 1990 by Dave Schreiber");
   
   
   SetAPen(FNWindow->RPort,1);
   Move(FNWindow->RPort,6,19);
   Text(FNWindow->RPort,"Directory",9);
   Move(FNWindow->RPort,6,154);
   Text(FNWindow->RPort,"Pattern",7);
   Move(FNWindow->RPort,6,165);
   Text(FNWindow->RPort,"Filename",8);
   
      /*Activate the directory string gadget, for user's convenience*/
      /*Note!  Window must be active for ActivateGadget to work on a*/
      /*string window!  Take this into account in writing your own code.*/
      /*I had to learn the hard way...:-( */
   
   ActivateGadget(&DirName,FNWindow,NULL);
   
   /*Main loop:  While requests for reading a new directory keep coming in...*/
   while((status = GetName(DirLock)) == GN_NEWDIR)
      {                             /*keep doing it...*/
      UnLock(DirLock);  /*Transfer lock to new directory*/
      RefreshGList(&DirName,FNWindow,NULL,1);
      DirLock=(struct FileLock *)Lock(DirNameSIBuff,ACCESS_READ);
      ActivateGadget(&filename,FNWindow,NULL); /*Activate filename gadget*/
      }  /*Loop back, read the new directory, and wait for input again*/

   UnLock(DirLock); /*Or we could be finished*/
   if(status) /*If OK pressed...*/
   {
      strcpy(fn,filenameSIBuff); /*Setup the return parameters*/
      strcpy(dn,DirNameSIBuff);
      strcpy(en,ExtNameSIBuff);
      if(cn) /*If cn isn't NULL, there's space for a string, so...*/
      {        /*construct the full filename for the host*/
         strcpy(cn,DirNameSIBuff);
         if( (dn[strlen(dn)-1] != ':') && strlen(dn)  && dn[strlen(dn)-1]!='/')
            strcat(cn,"/");
         strcat(cn,filenameSIBuff);
      }
   }

   CloseWindow(FNWindow); /*Close the window*/
   ResetGadgets();
   return(status);      /*and return*/
}

GetName(Lock)     /*Gets filenames in the directory associated with the*/
struct FileLock *Lock;  /*lock, and also gets user input*/
{
   struct fibHeader header;
   int status3,status2,status;
   USHORT FNNumber=0;

   header.first=header.directory=NULL;
   if(GetFib(&header)==FALSE)
      return(FALSE);
   SliderSInfo.VertPot=0;
   UpdateSlider(1,&SliderSInfo,&Slider);
   ClearFileBox();
   if(strnicmp(DirNameSIBuff,"PIPE:",5)) /*Check for pipe: device*/
      if(Lock!=NULL) /*If the lock (& the directory) exist...*/
         {
         Examine(Lock,&(CRNT));  /*Get the first entry*/
         if(GetFib(&header)==FALSE)  /*Get next node*/
            return(NULL);
         while((status3=ExNext(Lock,&(CRNT)))) /*Loop while there are files*/
            {
            if((status2=((CheckExt(CRNT.fib_FileName) ||
                  CRNT.fib_DirEntryType > 0) && /*If the specified extension*/
                  CheckInfo(CRNT.fib_FileName)) )) /*(if any) matches and*/
               {              /*it isn't a .info file (if masked)*/
               if(FNNumber<10)   /*display it...*/
                  PrintFileName(&(CRNT),Rp,XBorder,YBorder+(10*FNNumber++));
               else  /*or update the slider (if the filename box is full)*/
                  UpdateSlider(++FNNumber,&SliderSInfo,&Slider);
               }
            while((status=CheckIntuiMessages(FNNumber,&header))!=GN_DONE)
               {  /*After getting a name, checked for IDCMP messages*/
               if(status==GN_PARENT) /*If PARENT was pressed*/
                  {
                  Parentize(DirNameSIBuff); /*Setup the parent dir*/
                  RefreshGList(&DirName,FNWindow,NULL,1);
                  FreeFuncMemory(header.directory); /*Free the filename*/
                  return(GN_NEWDIR);   /*list and return*/
                  }
               if(status < GN_PARENT)
                  {
                  FreeFuncMemory(header.directory);
                  return(status);   /*For OK,NEWDIR, & CANCEL*/
                  }
               }
            if(status2) /*If the filename was valid, append another Fib*/
               if(GetFib(&header)==FALSE) /*structure for the next filename*/
                  return(NULL);
              /*If not valid, let the next filename info overwrite the current*/
              /*Fib*/
            }
            header.current->prev->next=NULL; /*Delete the last (unused) Fib*/
            if(header.directory->next == header.current) /*If no files*/
               header.directory->next=header.first=header.last=
                     header.top=NULL;     /*NULL certain pointers*/
            FreeMem(header.current,sizeof(struct fibNode));
         }
   for(;;)  /*Loop until the users says 'Change something!'*/
      {
      Wait(1<<FNWindow->UserPort->mp_SigBit);
      while((status=CheckIntuiMessages(FNNumber,&header))!=GN_DONE)
         {
         if(status==GN_PARENT)   /*PARENT pushed*/
            if(Parentize(DirNameSIBuff))
               {
               RefreshGList(&DirName,FNWindow,NULL,1);
               FreeFuncMemory(header.directory);
               return(GN_NEWDIR);
               }
         if(status < GN_PARENT)  /*Something else pushed*/
            {
            FreeFuncMemory(header.directory);
            return(status);   /*For OK,NEWDIR, & CANCEL*/
            }
         }
      }
}

CheckInfo(name)  /*Check to see if .info's should be printed*/
char *name;
{
   if(!(Info.Flags & SELECTED))
      return(TRUE);

   return(!(strcmp(&name[strlen(name)-5],".info")==0));
}

void ClearFileBox() /*Clear the box with the filenames in it*/
{
   SetAPen(Rp,0);
   RectFill(Rp,XBorder,YBorder-6,(25*8)+XBorder,YBorder+(100)-4);
   SetAPen(Rp,1);
}

BYTE Parentize(str) /*Convert a directory identifier string into the one*/
char *str;        /*for its parent directory*/
{
   int c,stat;

   if(str[0]==NULL)  /*If there is no directory explicitly specified*/
   {
      strcpy(str,"/");  /*Go to the parent of the current directory*/
      return(TRUE);
   }
      
   for(c=0,stat=TRUE;str[c]!=NULL && stat;c++)
      stat=(str[c]=='/');  /*If nothing but /'s, add another /*/
      
   if(stat)
   {
      strcat(str,"/");
      return(TRUE);
   }
      
   if(str[strlen(str)-1]==':')   /*If ':' is the last character*/
      return(FALSE);          /*We're at the root, and cannot back up*/
   
   stat=FALSE;
   
   for(c=strlen(str)-1;c >= 0;c--)
      if(str[c]=='/')   /*Delete last '/' & everything after that*/
      {
         if(c==0 || str[c-1]=='/')
            str[c+1]=NULL;
         else
            str[c]=NULL;
         c=0;
         stat = TRUE;
      }
      else
         if(str[c]==':')   /*Or delete everything after the ':'*/
         {              /*(which leaves the root)*/
            str[c+1]=NULL;
            c=0;
            stat=TRUE;
         }
   if(!stat)   /*If neither a / or a : were found, delete the whole thing*/
      strcpy(str,"");
   return(TRUE);
}

void UpdateSlider(Amt,Info,Gadgt) /*Update the slider bar to reflect*/
USHORT Amt;             /*the number of file/dir names available for selection*/
struct PropInfo *Info;
struct Gadget *Gadgt;
{
   Info->VertBody=((0xFFFF*10)/Amt);
   RefreshGadgets(Gadgt,FNWindow,NULL);
}

CheckIntuiMessages(Max,header) /*Check and respond to the window's IDCMP*/
USHORT Max;             /*messages*/
struct fibHeader *header;
{
   struct fibNode *cur;
   UBYTE c,line;
   struct IntuiMessage *mesg;
   struct Gadget *InUse;
   ULONG Class,Seconds,Micros;
   static ULONG OldSeconds=0;
   static ULONG OldMicros=0;
   USHORT Code;
   SHORT X,Y;
   char temp[150];

   /*Assumes that the calling function has either Wait()ed or wants to*/
   /*handle any IDCMP messages that have piled up*/
   
   while((mesg=(struct IntuiMessage *)GetMsg(FNWindow->UserPort))!=NULL)
      {
      InUse=(struct Gadget *)mesg->IAddress;
      X=mesg->MouseX;
      Y=mesg->MouseY;
      Class=mesg->Class;
      Code=mesg->Code;
      Seconds=mesg->Seconds;
      Micros=mesg->Micros;
      ReplyMsg(mesg); /*Reply to the message*/
      switch(Class)  /*Decide what happened*/
         {
         case MOUSEMOVE: /*If the mouse moved, then the silder is down, so*/
            if(Max > 10) /*update the filenames in the box to reflect the*/
               UpdateList(SliderSInfo.VertPot,Max,header); /*slider's*/
            break;                                               /*position*/
         case GADGETUP:
            if(InUse->GadgetID <= 10)
            {  /*One of the device gadgets was pushed*/
               if(strnicmp(InUse->GadgetText->IText,"PIPE:",5))
               {        /*Find the volume name if it isn't the PIPE:*/
                  MakeAbsName(InUse->GadgetText->IText);
                  strcpy(DirNameSIBuff,absDirName);
                  strcat(DirNameSIBuff,":");
               }
               else
                  strcpy(DirNameSIBuff,InUse->GadgetText->IText);
               RefreshGList(&DirName,FNWindow,NULL,1);
               return(GN_NEWDIR);
            }
            switch(InUse->GadgetID)
            {
               case 14: /*CANCEL was pushed*/
                  return(GN_CANCEL);
               case 12: 
               case 13: /*OK was pushed or return was pressed in the*/
                 return(GN_OK);  /*filename's box*/
               case 15:
                  return(GN_PARENT); /*PARENT was pushed*/
               case 18:  /*Return was pushed in file pattern box*/
                  if(ExtNameSIBuff[0]==NULL) /*If a blank field*/
                  {
                     strcpy(ExtNameSIBuff,"#?"); /*give a global file pattern*/
                     RefreshGList(&ExtName,FNWindow,NULL,1);
                  }
                  strcpy(temp,ExtNameSIBuff);
                  MasterPattern=(pattern *)CompilePattern(temp);
               case 11:  /*Return was pushed in the filename box*/
                  return(GN_NEWDIR);  /*a new one*/
            }
            return(GN_RIEN);     /*Nothing important*/
         case DISKINSERTED:
            PutDevicesInGadgets(FNWindow,DISKINSERTED);
            return(GN_RIEN);
         case DISKREMOVED:
            PutDevicesInGadgets(FNWindow,DISKREMOVED);
            return(GN_RIEN);
         case GADGETDOWN:     /*The .info gadget was pressed?*/
         if(InUse->GadgetID == 18)
            return(GN_NEWDIR);

            /*A file/dir name in the box was clicked on*/
         if(strnicmp(DirNameSIBuff,"PIPE:",5)) /*If the pipe:*/
            {                             /*don't try & get a filename*/
            line=(Y-YBorder+7)/10;    /*Get line clicked on*/
            
            for(c=0,cur=(struct fibNode *)header->top;c!=line && cur!=NULL;c++)
               cur=(struct fibNode *)cur->next; /*Find the file's Fib*/
               
            if(cur != NULL)   /*If the file exists*/
               if(cur->fib.fib_DirEntryType > 0)
               {        /*It's a directory, so read that directory*/
                  if(DirNameSIBuff[strlen(DirNameSIBuff)-1] != ':' && 
                        DirNameSIBuff[strlen(DirNameSIBuff)-1] != '/')
                     if(DirNameSIBuff[0]!=NULL)
                        strcat(DirNameSIBuff,"/"); 
                        
                  strcat(DirNameSIBuff,cur->fib.fib_FileName);
                  RefreshGList(&DirName,FNWindow,NULL,1);
                  return(GN_NEWDIR);
               }
               else   /*otherwise, return the filename clicked upon*/
               {
                  if(strcmp(filenameSIBuff,cur->fib.fib_FileName)==0 && 
                        DoubleClick(OldSeconds,OldMicros,Seconds,Micros))
                     return(GN_OK); /*Double-click == OK*/
                  strcpy(filenameSIBuff,cur->fib.fib_FileName);
                  RefreshGList(&filename,FNWindow,NULL,1);
                  OldSeconds=Seconds;  /*Store the time values*/
                  OldMicros=Micros; 
               }
            }
         }
      }
   return(GN_DONE);  /*All done...*/
}

void UpdateList(Pos,Num,header) /*Update filenames in the filename box*/
USHORT Pos;                   /*According to the slider's position*/
USHORT Num;
struct fibHeader *header;
{
   static USHORT LastFirstItem=30000;
   ULONG FirstItem;
   FirstItem=(((Pos+1)*(Num-10)) >> 16);

   if( FirstItem != LastFirstItem)
      RewriteList(FirstItem,header);
   LastFirstItem=FirstItem;
}

void RewriteList(First,header)  /*Rewrite the list of filenames in the*/
USHORT First;                 /*filename box...*/
struct fibHeader *header;
{
   register struct fibNode *node;
   register USHORT c,Y;

   if(First)   /*If we're not starting at the start...*/
      for(c=0,node=(struct fibNode *)header->first;c < First;c++)
         node=(struct fibNode *)node->next; /*Get the first filename*/
   else     /*Otherwise, start at the top*/
      node=(struct fibNode *)header->first;
   header->top=(struct fibNode *)node;
   
   /*Print the filenames (up to 10)*/
   for(c=0,Y=YBorder;(c < 10) && (node!=NULL);
         c++,Y+=10,node=(struct fibNode *)node->next)
      PrintFileName(&(node->fib),Rp,XBorder,Y);
}

BOOL CheckExt(name) /*Check to see if 'name' has the supplied extension*/
char *name;
{
   return(MatchPattern(MasterPattern,name));
}

BYTE GetFib(header)
struct fibHeader *header;
{
   header->last=(struct fibNode *)header->current;
   if((header->current=(struct fibNode *) /*Allocate a FIB structure*/
         AllocMem(sizeof(struct fibNode),MEMF_PUBLIC|MEMF_CLEAR))==NULL)
      {
      FreeFuncMemory(header->current); /*Whoops, it didn't work...*/
      return(FALSE);
      }
   if(header->directory==NULL) /*Must want it for a directory...*/
      header->directory=(struct fibNode *)header->current;
   else  /*Append to the current FIB list*/
      {
      if(header->first==NULL)
         {
         header->directory->next=header->first=header->last=header->top=
               (struct fibNode *)header->current;
         CopyFIB(&(header->directory->fib),&(header->current->fib));
         }
      else
         {
         CopyFIB(&(header->last->fib),&(header->current->fib));
         header->last->next=(struct fibNode *)header->current;
         header->current->prev=(struct fibNode *)header->last;
         }
      }
   return(TRUE);
}

void CopyFIB(from,to) /*Copy a fileinfoblock from 'from' to 'to'*/
struct FileInfoBlock *from,*to;
{
   to->fib_DiskKey=from->fib_DiskKey;
   to->fib_DirEntryType=from->fib_DirEntryType;
   to->fib_Protection=from->fib_Protection;
   to->fib_EntryType=from->fib_EntryType;
   to->fib_Size=from->fib_Size;
   to->fib_NumBlocks=from->fib_NumBlocks;
   strcpy(to->fib_FileName,from->fib_FileName);
   strcpy(to->fib_Comment,from->fib_Comment);
   to->fib_Date.ds_Days=from->fib_Date.ds_Days;
   to->fib_Date.ds_Minute=from->fib_Date.ds_Minute;
   to->fib_Date.ds_Tick=from->fib_Date.ds_Tick;
}

void PrintFileName(fib,RPrt,X,Y)  /*Print a filename in the filename box*/
struct FileInfoBlock *fib;
struct RPort *RPrt;
USHORT X,Y;
{
   UWORD len;
   char name[40];
   
   strcpy(name,fib->fib_FileName);
   if(fib->fib_DirEntryType > 0) /*Set the pen color, according to whether*/
   {        /*we're printing a filename or a directory name*/
      SetAPen(RPrt,yafr_e_DirnamePenColor); 
      strcat(name,"  (dir)"); /*Append '  (dir)' if it's a directory*/
   }
   else
      SetAPen(RPrt,yafr_e_FilenamePenColor);
   Move(RPrt,X,Y);
   Text(RPrt,"                         ",25);  /*Blank out the space*/
   Move(RPrt,X,Y);
   len = (strlen(fib->fib_FileName)<=25) ? strlen(fib->fib_FileName) : 25;
   Text(RPrt,fib->fib_FileName,len);   /*Trundicate the filename at 25 chars*/
                                       /*and print it.*/
}

void FreeFuncMemory(first)   /*Free the memory taken up by a fib list*/
register struct fibNode *first;
{
   register struct fibNode *next,*current;
   next=(struct fibNode *)first->next;
   FreeMem(first,sizeof(struct fibNode));
   while((current=(struct fibNode *)next)!=NULL)
      {
      next=(struct fibNode *)current->next;
      FreeMem(current,sizeof(struct fibNode));
      }
}

void PutDevicesInGadgets(Wdw,stat)
struct Window *Wdw;
ULONG stat;
{

   /*structure definitions*/
   struct RootNode *root;
   struct DosInfo *dosinfo;
   struct DeviceList *devlist;
   struct Gadget *button,*onbutton,*lastunghosted;
   UBYTE c=0;
   UBYTE old_c;
   
   char devname[35]; /*This is where the device name (DH0:, DF0:, etc.) will
                        end up*/

   /*Get the first entry in the list of devices*/   
   root=(struct RootNode *)DOSBase->dl_Root;
   dosinfo=(struct DosInfo *)BADDR(root->rn_Info);
   devlist=(struct DeviceList *)BADDR(dosinfo->di_DevInfo);

   /*Go through the linked list of devices*/

   for(button=(struct Gadget *)&df0 ; c<10 && devlist ; devlist=
      (struct DeviceList *)BADDR(devlist->dl_Next))

      if(devlist->dl_Type==DLT_DEVICE && devlist->dl_Task && 
      	IsDOSDisk(devlist)) /*If it's a disk-type (RAM, hard, floppy) drive:*/

      {
         bstrcpy(devname,(BPTR)devlist->dl_Name); /*Convert the BSTR to a*/
         strcat(devname,":");    /*regular string and add a ':'*/
         
	      /*Devices with removable media must have the media inserted before*/
         /*they'll show up.  Of course, this is meaningless when talking*/
         /*about non-removable hard drives, RAM drives, etc.*/
	 
	      /*Copy the text to the Gadget text area*/
	      strcpy(button->GadgetText->IText,devname);
         c++;
         lastunghosted=(struct Gadget *)button;
         button=(struct Gadget *)button->NextGadget;
      }
   if(Wdw!=NULL)
      SetAPen(Rp,0);

      /*Clear & switch on gadgets that'll are in use now*/
   
   old_c=c;
   if(stat!=DISKREMOVED)
      if(FirstDisabled != NULL)
         for(onbutton=(struct Gadget *)FirstDisabled;button!=NULL && 
                  onbutton!=(struct Gadget *)button && 
                  onbutton!=(struct Gadget *)&pipe;
                  onbutton=(struct Gadget *)onbutton->NextGadget)
            if(Wdw == NULL)
               onbutton->Flags -= GADGDISABLED;
            else
            {
               RectFill(Rp,onbutton->LeftEdge,onbutton->TopEdge,
                     onbutton->LeftEdge+onbutton->Width-1,
                     onbutton->TopEdge+onbutton->Height-1);
               OnGadget(onbutton,Wdw,NULL);
            }
         
      
   FirstDisabled=(struct Gadget *)button; /*Store the first ghosted gadget*/
   
   if(stat!=DISKINSERTED)
      for(;c < 10 && !(button->Flags && GADGDISABLED);c++,
            button=(struct Gadget *)button->NextGadget)
      {  /*Clear out any unused gadget text & deactivate unused gadgets*/
         strcpy(button->GadgetText->IText,"    ");
         if(Wdw==NULL)
            button->Flags += GADGDISABLED;
         else
            OffGadget(button,Wdw,NULL);
      }
      
   if(Wdw != NULL)
      RefreshGList(&df0,Wdw,NULL,old_c); /*Refresh the gadgets*/
}

void ResetGadgets()
{
   struct Gadget *gadg;
   
   for(gadg=(struct Gadget *)&df0;gadg!=(struct Gadget *)&pipe;
         gadg=(struct Gadget *)gadg->NextGadget)
      gadg->Flags = NULL;

}

void bstrcpy(dest,source)  /*Copy a BSTR to a regular string*/
BSTR source;
char *dest;
{
   register UBYTE *ch;
   register int len,i;
   
   ch=(UBYTE *)BADDR(source);
   len=(int) *(ch++);
   len=(len>20)?20:len;
   
   for (i=0;i<len;i++)
      dest[i]=*(ch++);
   dest[i]=NULL;
}

BYTE IsDOSDisk(devlist)    /*Check to see if a device in a disk-type device*/
struct DeviceList *devlist;  /*(the sort of device that shows up when the*/
{                          /*AmigaDOS INFO command is run)*/
   struct MsgPort *port;
   BYTE result;            
   struct InfoData *info;  
   struct StandardPacket *diskpacket;

  	result=FALSE;  
   
   /*Allocate memory for the Port to use*/
   info=(struct InfoData *) AllocMem(sizeof(struct InfoData),
         MEMF_CLEAR|MEMF_PUBLIC);
   if(info==NULL)
      return(FALSE);
   
   diskpacket=(struct StandardPacket *) 
         AllocMem(sizeof(struct StandardPacket),MEMF_CLEAR|MEMF_PUBLIC);
   if(diskpacket==NULL)
   {
      FreeMem(info,sizeof(struct InfoData));
      return(FALSE);
   }
   
   /*Allocate the port*/
   port=(struct MsgPort *) CreatePort(NULL,0);
   if(port==NULL)
   {
      FreeMem(info,sizeof(struct InfoData));
      FreeMem(diskpacket,sizeof(struct StandardPacket));
      return(FALSE);
   }  
   
   /*Set up the message to send to the device*/
   diskpacket->sp_Msg.mn_Node.ln_Type=NT_MESSAGE;
   diskpacket->sp_Msg.mn_Node.ln_Name=(char *)&diskpacket->sp_Pkt;
   diskpacket->sp_Msg.mn_ReplyPort=port;
   diskpacket->sp_Pkt.dp_Link=&diskpacket->sp_Msg;
   diskpacket->sp_Pkt.dp_Type=ACTION_DISK_INFO;
   diskpacket->sp_Pkt.dp_Arg1=((LONG)info>>2);
   diskpacket->sp_Pkt.dp_Port=port;
   
   /*Send the message*/
   PutMsg(devlist->dl_Task,&diskpacket->sp_Msg);
   
   /*and wait for a reply...*/
   WaitPort(port);
   
   /*Store the result*/
   result=(info->id_DiskType==ID_DOS_DISK);

   /*Deallocate resources*/
   DeletePort(port);
   FreeMem(info, sizeof(struct InfoData));
   FreeMem(diskpacket, sizeof(struct StandardPacket));
   
   /*And return the result*/
   return(result);
}


/*!*******************************************************************
 * FindRoot by Bruce Rogers 1/20/90
 *********************************************************************/

/*!*******************************************************************
 * Kludges disk:name/directory with filename so we can just read it.
 *********************************************************************/
void    DosConcatenate(string1,string2,result)
char    string1[],string2[],result[];
{
short   i,j;

    for(i=0; string1[i] != '\0'; i++)
        result[i]=string1[i];
    if ((result[i-1] != ':') && (result[i-1] != '/') && (i!=0) && string2[0]!=0)
    {
        result[i]='/';
        ++i;
    }
    for(j=0; string2[j] != '\0'; j++)
        result[i+j] = string2[j];
    result[i+j] = '\0';
}

/*!*******************************************************************
 * Recursively go up parent chain, looking for oldest parent.
 * Create the absolute path string as we go.
 *********************************************************************/
SeekRoot(oldlock)
ULONG   oldlock;
{
short   i;
ULONG   newlock;
struct  FileInfoBlock *fileInfo;

    fileInfo=(struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),
          MEMF_CLEAR);
    if(fileInfo==NULL)
       return(FALSE);
    Examine(oldlock,fileInfo);
    DosConcatenate(fileInfo->fib_FileName,absDirName,absDir);
    for(i=0;i<MAXPATHSTRING;i++) absDirName[i]=absDir[i];
    newlock=ParentDir(oldlock);
    UnLock(oldlock);
    oldlock=newlock;
    if (oldlock!=NULL) SeekRoot(oldlock);
    FreeMem(fileInfo,sizeof(struct FileInfoBlock));

}

/*!*******************************************************************
 * Given a path, sets absDirName equal to the absolute path
 *********************************************************************/
        MakeAbsName(dirName)
char    *dirName;
{
short   i;
ULONG   lock;

    for(i=0;i<MAXPATHSTRING;i++) absDirName[i]=absDir[i]=0;
    lock=Lock(dirName,ACCESS_READ);
    if (lock==NULL)
      return(NULL);
    SeekRoot(lock);
    for(i=0;i<MAXPATHSTRING;i++)
    {
        if (absDirName[i]=='/')
        {
            absDirName[i]=':';
            break;
        }
    }
}

/*End of YAFR.c*/
