/****************************************************************************
 *
 * $Source: /cvsroot/riscossmbserver/smbserver/src/unix/c/dirent,v $
 * $Date: 2000/04/17 16:50:48 $
 * $Revision: 1.9 $
 * $State: Exp $
 * $Author: david $
 *
 * $Log: dirent,v $
 * Revision 1.9  2000/04/17 16:50:48  david
 * new parateter hexFileTypes
 *
 * Revision 1.8  2000/04/02 17:00:30  david
 * Changes to allow this to be compiled as a RISCOS module. Mainly
 * fix problems with stack depth which is limited in SVC mode.
 *
 * Revision 1.7  2000/02/13 21:05:22  david
 * Modified following testing with !LanMan98
 *
 * Revision 1.6  2000/02/08 20:19:10  david
 * Added detect !lanman 98, detect extensions, tidied file type stuff,
 * changed duplicate filename delimeter (for files with same name but
 * different extension) from / to ALT+\
 *
 * Revision 1.5  1999/11/23 22:05:11  david
 * Increased filename length to MAXPATHLEN
 * uname and dirent - support for spaces in a filename which
 * is translated to 0xa0 on RISCOS and mapping of obscure DOS
 * filename characters to RICOS characters as 2-327 or the PRMs
 *
 * Revision 1.4  1999/11/07 15:17:20  david
 * dirent: added stuff to support appendFiletype
 * getcwd: addet getFs and variations on getcwd
 *
 * Revision 1.3  1999/07/20 21:02:18  david
 * Append filetype to end of filename.
 *
 * Revision 1.2  1999/06/20 13:48:42  david
 * Minor change to help debugging.
 *
 * Revision 1.1  1999/05/16 12:00:09  david
 * Initial revision
 *
 * Revision 1.4  1996/10/30 21:59:01  unixlib
 * Massive changes made by Nick Burret and Peter Burwood.
 *
 * Revision 1.3  1996/09/16 21:23:52  unixlib
 * CL_0002 Nick Burret
 * Minor changes to file handling
 * Change most error numbers, and use in assembler sources (SJC)
 * Various minor bug fixes and compatability changes.
 *
 * Revision 1.2  1996/05/06 09:01:35  unixlib
 * Updates to sources made by Nick Burrett, Peter Burwood and Simon Callan.
 * Saved for 3.7a release.
 *
 * Revision 1.1  1996/04/19 21:35:27  simon
 * Initial revision
 *
 * Jan 1994: Alun Jones, auj@aber.ac.uk
 *
 * As supplied, UnixLib can only hold one directory open.
 * If you try to open two, it returns a valid directory pointer,
 * but overwrites the old version. For example, this stops tar from
 * recursing the directory tree. dirent.c is a fixed version of
 * UnixLib's dirent.
 ***************************************************************************/

static const char rcs_id[] = "$Id: dirent,v 1.9 2000/04/17 16:50:48 david Exp $";

#if 0

#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/unix.h>
#include <sys/syslib.h>
#include <sys/os.h>
#include <sys/types.h>
#include <sys/swis.h>

#include <dirent.h>
#else

#include "includes.h"
#include "swis.h"
#include "my_os.h"
 
#define DIRSIZ(dp) (((sizeof(struct dirent) - \
	MAXNAMLEN + ((dp)->d_namlen + 1)) + \
	(sizeof(int) - 1)) & ~(sizeof(int) - 1))



#endif

#define DIRBUFSIZ	1024
#define DIRBUFSHIFT	10

#define MAXDIRS		32
static struct
  {
    struct dirent *__dirval;
    DIR *which;
  }
dirvals[MAXDIRS];

BOOL appendFileType=True;
BOOL expandZips=True;                          
BOOL detectLanMan98;     
BOOL hexFileTypes;
BOOL detectExtension;

extern void get_ftype(int aftype,char *ext);
extern void writeLog(char *s, int fd, char *buf, int n);
                                      
extern void checkImageFile(char*,BOOL);
    
typedef struct sDosChars {
  char riscOsChar;
  char dosChar;
} tDosChars;

const tDosChars dosChars[] = {
  { '#', '?' },
  { '?', '#' },
  { '+', '&' },
  { '=', '@' },
  { ';', '%' },
  { '<', '$' },
  { '>', '^' },
  { 0xa0,' ' },
  {  0 ,  0  }
};

void convertToDos(char *s)
{
  int  i;

  while (*s!=NULL)
  {       
    i=0;
    while (dosChars[i].riscOsChar!=0)
    {                            
      if (*s==dosChars[i].riscOsChar)
      {
        *s=dosChars[i].dosChar;
        break; 
      }
      i++;
    }  
    s++;
  }
}
  
char filename[MAX_FILE_NAME_LENGTH];

void convert_name2(char *name, char *dname)
{
  char *slash;
  int regs[10];
  _kernel_oserror *e;
  char fileext[5],fileext2[5];
#if 0
#if 0
  char filename[MAX_FILE_NAME_LENGTH];
#else
  static char *filename=NULL;
#endif
#endif

  char *rname;
  int  t_ftype;
  BOOL isDir=False,isindosdisc=False;
  char slashprime = 0xb7; /* ALT + '\' = '' */
         
#if 0                   
  if (filename==NULL)
  {
     filename = (char*)calloc(1,MAX_FILE_NAME_LENGTH);
     if (filename==NULL)
     {
       DEBUG(0,("calloc failed\n"));
       return;
     }
  }
#endif

  rname = __uname (dname, 0);

  DEBUG(3,("convert_name %s,%s\n",rname,name));
                                    
  isindosdisc = isInDosDisc(name,False);      
  if (strcmp(name,"..")!=0 && strcmp(name,".")!=0 && appendFileType==True /*&&
      (isindosdisc==False || detectLanMan98==True)*/)
  {
                   
    sprintf(filename,"%s.%s",rname,name);
/*
    if (e = myos_file (20, filename, regs))
*/                       
          
    regs[0]=5 /*20*/;
    regs[1]=(int)filename;
    if (e = myos_swi (OS_File, regs))
    {

    }
    else
    {
       t_ftype=((regs[2] & 0xfff00) >> 8);         
#if 0
       if (regs[6]!=-1/* && regs[6]!=0x1000 && regs[6]!=0x2000*/)
#endif
       if (regs[0]!=0)
       {          
#if 0
          t_ftype=regs[6];
          if (regs[6]!=0x1000 && regs[6]!=0x2000)
          {
              get_ftype(regs[6],fileext);   
          }
          else
          {
              if ((((regs[2] & 0xfff00) >> 8) == 0xddc) && expandZips==False)
              {
                 t_ftype=0xddc;
                 regs[6]=0xddc;
                 get_ftype(regs[6],fileext); 
              }
              else
              {  
                 isDir=True;
                 regs[6]=0;
              }
          }
#endif
          if (regs[0]==1)
          {
             get_ftype(t_ftype,fileext);             
          }                              
          else
          {
              if ((t_ftype == 0xddc) && expandZips==False)
              {
                 t_ftype=0xddc;
                 get_ftype(t_ftype,fileext); 
              }
              else
              {  
                 isDir=True;
                 t_ftype=0;
              }
          }

          if (regs[0]==3)
          {
             checkImageFile2(filename,False,t_ftype);
          }

          DEBUG(5,("fileEXT %s ext %s, %d, %x, %d, %x\n",filename,fileext,regs[0],t_ftype,expandZips,regs[2]));
           
          if (detectLanMan98==True || hexFileTypes==True)
          {
             if (t_ftype!=0 && isDir==False)
             {
               sprintf(fileext2,",%.3x",t_ftype);
               strcat(name,fileext2); 
             }
          }
          else
          {
                               
             slash=strrchr(name,slashprime);
             if (slash!=NULL)
             {
               if (/*regs[6]*/t_ftype==0)
               {
                  *slash='\0';
               }
               else
               {   
                 *slash='/';
                 strcpy((char*)(slash+1),fileext);
               }  
             }
             else
             {
             slash=strrchr(name,'/');
             if (slash!=NULL && isDir==False)
             {    
                /* 
                 * if there's some sort of extension then leave it alone if
                 * detectExtension is true - note that if !smbserver created the
                 * file then we could be stuffed if detectExentsion is True
                 * perhaps smbserver should set the top bit in a character so
                 * we can spot files it has created?
                 */                 
                if (detectExtension==False && isindosdisc==False)
                {
                  if (/*regs[6]*/t_ftype==0)
                  {
                    *slash='\0';
                  }
                  else                                                         
                  { 
                    /* 
                     * If !smbserver created the filename in might be fred/0 if there
                     * already was a file called fred so we want to replace 0 with the
                     * file type. 
                     * However if !smbserver did not create this file the extension
                     * might be legitimate and we are stripping it incorrectly!
                     */
                    if (strlen((char*)(slash+1))<3 &&
                        tolower(*(slash+1))==tolower(fileext[0]))
                    {
                       strcpy((char*)(slash+1),fileext); 
                    } 
                    else
                    if ( (strcmp(fileext,"txt")!=0) &&
                         (t_ftype!=0xfe4) &&
                         (tolower(*(slash+1))!=tolower(fileext[0])))
                    { 
                      strcat(name,"/");  
                      strcat(name,fileext);
                    }
                  }
                }
             
             }             
             else
             {
               if (/*regs[6]*/t_ftype!=0 && t_ftype!=0xfe4)
               {
                 strcat(name,"/");
                 strcat(name,fileext);
               }
             }
             }
          }
       }
    }
   
  }
             
  slash=strchr(name,'/');

  while (slash!=NULL)
  {
    if (slash!=NULL)
    {
       *slash='.';
    }
    slash=strchr(name,'/');
  }

  convertToDos(name);

  DEBUG(3,("convert_name result %s\n",name));  
}


/* opendir() */

DIR *opendir (const char *name)
{
  int i;
  int r[10];
  _kernel_oserror *e;
  register DIR *d;
  char *rname;

  rname = __uname ((char *)name, 0);

  if (e = myos_file (0x05, rname, r))
    {
      /*__seterr (e);*/
      return (0);
    }

  if (r[0] != 2 && r[0] != 3)	/* pjb, added support for images directories */
    return (0);

  if (!(d = (DIR *) malloc (sizeof (DIR))))
    return (0);
  if (!(d->dd_buf = malloc (DIRBUFSIZ)))
    {
      free (d);
      return (0);
    }
  /* We have to store the original format of the name in the
     dirent buffer. The user program might perform some sort of
     hashing and hope that it's copy of the name is the same
     as that of the dirent version.  */
  if (!(d->dd_name = __permstr (name)))
    {
      free (d->dd_buf);
      free (d);
      return (0);
    }
  d->dd_fd = 0;
  d->dd_loc = 0;
  d->dd_size = 0;
  d->dd_bsize = DIRBUFSIZ;
  d->dd_off = 0;
  d->dd_off2 = 0;

  for (i = 0; i < MAXDIRS; i++)
    {
      if (!dirvals[i].__dirval)
	{
	  if (!(dirvals[i].__dirval = (struct dirent*) malloc (sizeof (struct dirent))))
	    {
	      free (d->dd_name);
	      free (d->dd_buf);
	      free (d);
	      return (0);
	    }
	  dirvals[i].which = d;
	  break;
	}
    }

  return (d);
}

/* __nextdir() */

static int __nextdir (register DIR * d)
{
  int r[10];
  _kernel_oserror *e;
  register int i;
  register char *s;
  char *rname;

  if (d->dd_off2 < 0)
    return (-1);

  /* Got to convert the filename on every usage since it could
     be Unix format.  */
  rname = __uname (d->dd_name, 0);
  r[0] = 9;
  r[1] = (int) rname;
  r[2] = (int) d->dd_buf;
  r[3] = 1; /* DIRBUFSIZ / MAXNAMLEN;*/
  r[4] = d->dd_off2;
  r[5] = DIRBUFSIZ;
  r[6] = (int) "*";

  DEBUG(6,("__nextdir %s, r[3]=%d, r[4]=%d\n",rname, r[3], r[4]));

  if (r[4]==0)
  {
    strcpy(d->dd_buf,".");
    r[3]=1;
    r[4]=1;
  }
  else
  if (r[4]==1)
  { 
    strcpy(d->dd_buf,"..");
    r[3]=1;
    r[4]=2;
  }
  else
  { 
    r[4] -= 2;
    if (e = myos_swi (OS_GBPB, r))
    {                    
      wimp_werr(0,e->errmess);
      /* __seterr (e);  */
      free (d->dd_name);
      d->dd_name=NULL;
      free (d->dd_buf);
      d->dd_buf=NULL;
      free (d);                   
      DEBUG(6,("myos_swi OS_GPBP failed\n"));
      return (-1);
    }         

    if (r[4]>=0)
    {
      r[4]+=2;
    }
  }

  for (i = r[3], s = d->dd_buf; i; i--)
    while (*++s);

  d->dd_loc = 0;
  d->dd_size = s - d->dd_buf;
  d->dd_off = (d->dd_off2 << DIRBUFSHIFT);
  d->dd_off2 = r[4];

  if (r[4] < 0 && !r[3])             
  {
    DEBUG(6,("r[4] < 0 && !r[3] is TRUE"));
    return (-1);
  }

  return (0);
}

/* readdir() */

#ifdef readdir
#undef readdir
#endif


struct dirent * my_readdir (DIR *d)
{
  register struct dirent *r = NULL;
  register int i;
  register char *s;
  int regs[10];
  _kernel_oserror *e;
  char fileext[5];
#if 0
#if 0
  char filename[MAX_FILE_NAME_LENGTH];
#else
  static char *filename=NULL;
#endif

  if (filename==NULL)
  {
    filename = (char*) calloc(1,MAX_FILE_NAME_LENGTH);
    if (filename==NULL)
    {
      DEBUG(0,("calloc failed\n"));
      return NULL;
    } 
  }
#endif

  if (!d)
    return (0);

  for (i = 0; i < MAXDIRS; i++)
    {
      if ((dirvals[i].__dirval) && (dirvals[i].which == d))
	{
	  r = dirvals[i].__dirval;
	  break;
	}
    }


  if (!r)
    return (0);

  while (d->dd_loc >= d->dd_size)
    if (__nextdir (d))
      return (0);

  s = d->dd_buf + d->dd_loc;
  i = strlen (s);
  r->d_off = d->dd_off;
  i++;
  d->dd_off += i;
  d->dd_loc += i;
  if (i >= MAXNAMLEN)
    s[i = MAXNAMLEN - 1] = 0;
  memcpy (r->d_name, s, i);
  r->d_namlen = i;
  r->d_reclen = DIRSIZ (r);

  /* The file serial number. This is the same as the st_ino member
     that stat will return for the file.  */
  r->d_fileno = __get_file_serial_no (r->d_name);
       
  convert_name2(r->d_name,d->dd_name);

  return (r);
}

/* telldir() */

long telldir (DIR *d)
{
  if (!d)
    return (-1);

  return (d->dd_off);
}

/* seekdir() */

void seekdir (DIR *d,long off)
{
  if (!d)
    return;

  if (d->dd_off2 != (off >> DIRBUFSHIFT))
    {
      d->dd_off2 = (((int) off) >> DIRBUFSHIFT);
      d->dd_loc = 0;
      d->dd_size = 0;
      __nextdir (d);
    }

  d->dd_off = (size_t) off;
  d->dd_loc = (size_t) off & (DIRBUFSHIFT - 1);
}

/* rewinddir() */

void (rewinddir) (DIR *d)
{
  rewinddir (d);
}

/* closedir() */

int closedir (DIR *d)
{
  int i;

  DEBUG(5,("close dir 0x%x\n",d));

  if (!d)
    return (-1);

  for (i = 0; i < MAXDIRS; i++)
    {
      if (dirvals[i].__dirval && (dirvals[i].which == d))
	{
	  free (dirvals[i].__dirval);
	  dirvals[i].__dirval = 0;
	  dirvals[i].which = 0;
	  break;
	}
    }

  free (d->dd_name);
  free (d->dd_buf);                
  free (d);

  return (0);
}
