/************************************************/
/*   Demo program for sd_v100.lib               */
/*                                              */
/*   Hardware: uPAC-5001,iPAC-8000              */
/*                                              */
/************************************************/
/*
[2008,09,18] by Kevin
    first released.

[2008,09,21] by Kevin
    bug fixed
        1. do_rm
            Problem:can not perform command like "rm *.*" in a sub directory.
            Reason: In the pc_gnext loop, the file name given to pc_del 
                should be simeple file name (like: test1.txt) 
                or with full path (like: a:\demo\text1.exe)
                statobj.path gives empty path. that makes following code error.
                    sprintf(fullfilename,"%s\\%s",statobj.path,statobj.filename); 
            Solution:
                the correct code should be following    
                    sprintf(fullfilename,"%s",statobj.filename); 
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "..\..\Lib\G4500.h"            
#include "..\..\Lib\microSD.h"

char Prompt[20]="\r\nMicroSD>";
unsigned char shell_buf[1024];
char working_buf[100];      // used by lex: must be global


typedef struct dispatcher {
    char *cmd;
    int  (*proc)( int argc, char **argv);
    char *helpstr;
} DISPATCHER;

DISPATCHER *lex(void);

int do_quit(int agc, char **agv);
int do_help(int agc, char **agv);

int do_type(int agc, char **agv);
int do_mkdir(int agc, char **agv);
int do_rmdir(int agc, char **agv);
int do_del(int agc, char **agv);
int do_rm(int agc, char **agv);
int do_deltree(int agc, char **agv);
int do_move(int agc, char **agv);
int do_cwd(int agc, char **agv);
int do_ls(int agc, char **agv);
int do_type(int agc, char **agv);
int pc_seedir(char *path);
int domkfs(int agc, char **agv);
int do_setattr(int agc, char **agv);

int do_format(int agc, char **agv);

void prompt_user(char *prompt, char *buf);


int do_newCard(int agc, char **agv);


DISPATCHER cmds[] =   {
    { "", do_help, ""},
    { "CD", do_cwd, "CD PATH or CD to display PWD " },
    { "DEL",do_del,"Del File (delete 1 file)"},
    { "RM",do_rm, "RM File (delete files)" },
    { "DELTREE", do_deltree, "DELTREE PATH"},
    { "DIR", do_ls, "DIR PATH" },
    { "FORMAT", do_format,"FORMAT"},
    { "HELP", do_help, "HELP" },
    { "MKDIR", do_mkdir, "MKDIR PATH" },
    { "MOVE",do_move, "MOVE filename NEWNAME" },
    { "RMDIR", do_rmdir, "RMDIR PATH" },
    { "TYPE",do_type,"TYPE filename"},
    { "SETATTR", do_setattr, "SETATTR Name RDONLY|HIDDEN|SYSTEM|ARCHIVE|NORMAL"},
    { "NewCard", do_newCard, "NewCard" },
    { "QUIT", 0, "QUIT" },
};

int CmdNumber=sizeof(cmds)/sizeof(DISPATCHER);



// ********************************************************************
// THIS IS THE MAIN PROGRAM FOR THE TEST SHELL
// ********************************************************************
void  DoCommand(void)
{
    DISPATCHER *pcmd;
    do_help(0, 0);
    do 
    {
        pcmd = lex();
        if(pcmd) 
        {
            if (!pcmd->proc) break;
            pcmd->proc(Argc-1, Argv+1);
        }
        else break;
    }while(1);
}


int _Gets(char *str)
{ 
    int cnt=0;
    int data;
    unsigned cnt0=0; 

    do 
    {
        if(Kbhit())
        {
            data=Getch();
            if(data == '\r')
            {
                *str=0;
                Putch(data);
                Putch('\n');
                break;
            }
            else if(data == '\b')
            {
                if(cnt)
                {
                    str--;
                    cnt--;
                    Putch(data);
                    Putch(' ');
                    Putch('\b');
                }
            }
            else 
            {
                Putch(data);
                *str++=data;
                cnt++;
            }
        }
        else 
        {
            cnt0++;
            if(cnt0>2000)
            {
                cnt0=0;
                RefreshWDT();
            }
        }   
    }while(1);
    return cnt;
}

// ********************************************************************
// get next command; process history log
void do_prompt(char *working_buf)
{
    Print(Prompt);
    _Gets(working_buf);
}

static char *Delimiter=" ,\r\t";
int CmdToArg(char *cmd)
{
    char *cmd1;

    Argc=0;
    cmd1=strtok(cmd,Delimiter);
    while(cmd1){
        Argv[Argc]=cmd1;
        Argc++;
        cmd1=strtok(NULL,Delimiter);
    }
    return Argc;
}


DISPATCHER *lex(void)      /*__fn__*/
{ 
    int i;

    do 
    {
        do_prompt(working_buf);
        CmdToArg(working_buf);

        if(Argc>=1)
        {
            for(i=0;i<CmdNumber;i++)
            {
                if(!stricmp(Argv[0],cmds[i].cmd))
                {
                    return &cmds[i];
                }
            }
        }
    }while(1);
}


int do_help(int agc, char **agv)                             /*__fn__*/
{ 
    int nprinted = 0,i;

    (void)agc;
    (void)agv;
    for(i=0;i<CmdNumber;i++)
    {
        if(cmds[i].cmd) Print("%s\r\n", cmds[i].helpstr);
        if (nprinted++ > 20) 
        {
            Print("Press any key");
                Getch();
            Print("\r\n");
            nprinted = 0;
        }
    }
    return 1;
}


//======== Demo programs bellow ==========================/


/* MKDIR PATH */
int do_mkdir(int agc, char **agv) 
{
    if (agc == 1) 
    {
        if (pc_mkdir(*agv)) return (1);
        else                return (0);
    }
    Print("Usage: MKDIR D:PATH\r\n");
    return (0);
}

/* RMDIR PATH */
int do_rmdir(int agc, char **agv) 
{

    Print("\r\n%s  ",*agv);DelayMs(0);
    if (agc == 1) 
    {
        if (pc_rmdir(*agv))
        {
            Print(":remove dir ok\n\r");
            return (1);
        }
        else
        {
            Print(":remove dir error\n\r");
            return(0);
        }
    }
    Print("Usage: RMDIR D:PATH\r\n");
    return (0);
}

/* DELTREE PATH */
int do_deltree(int agc, char **agv)
{

    if (agc == 1)
    {
        if (pc_deltree(*agv))
        {
            Print("ok\n\r");
             return (1);
        }
        else
        {
            Print("error\n\r");
            return(0);
        }
    }
     Print("Usage: DELTREE D:PATH\r\n");
     return (0);
}

/* Delete files */
int do_rm(int agc, char **agv)
{
    DSTAT statobj;
    char fullfilename[100];
    int err;

     if (agc == 1)
     {
          /* Get the path */
          strcpy(fullfilename,*agv); //fullfilename can be *.*, *.exe, t*.*, etc.
          if (pc_gfirst(&statobj,fullfilename)) 
          {
                do
                {
                     /* Construct the file name to the current entry */
                     sprintf(fullfilename,"%s",statobj.filename);
                     if (!pc_isdir(fullfilename) && !pc_isvol(fullfilename))
                     {
                          if (pc_del( fullfilename ) )
                            Print("%s deleted.\r\n",fullfilename);
                          else
                          {
                            err=pc_get_errno();
                            Print("Can not delete: %s, error code=%d\r\n",fullfilename,err);
                        }
                     }
                }  while(pc_gnext(&statobj));
                pc_gdone(&statobj);
          }
          return(1);
     }
     Print("Usage: RM D:PATH\r\n");
     return(0);
}

/* Delete one file */
int do_del(int agc, char **agv) 
{
    char filename[15];
    int err;
    if (agc == 1)
    {
        /* Get the name */
        strcpy(filename,*agv);
        if (pc_del(filename))
            Print("%s deleted.\r\n",filename);
        else
            Print("Can not delete: %s\r\n",filename);
        
        err=pc_get_errno();
        Print("Error code=%d\n\r",err);
        return(1);
    }
    Print("Usage: DEL D:PATH\r\n");
    return(0);
}   

/* Move file NEWNAME */
int do_move(int agc, char **agv)
{
     int err;
     if (agc == 2)
     {
         if (pc_move(*agv , *(agv+1) ))
            return (1);
         else
         {
               err=pc_get_errno();
               if(err==FSERR_EXIST)
                   Print("File or directory already exists\r\n");
               else if(err==FSERR_NOSPC)
                   Print("Write failed\r\n");
               else if(err==FSERR_NOENT)
                   Print("Directory not found\r\n");
               return(0);
         }
    }
    Print("Usage:  Move File NEWNAME \r\n");
    return (0);
}


/* CD PATH */
int do_cwd(int agc, char **agv)
{
    char lbuff[100];

    if (agc == 1) 
    {
        if (pc_set_cwd(*agv))   return(1);
        else 
        {
            Print("?\r\n");
            return(0);
        }
    }
    else 
    {
        if (pc_get_cwd(lbuff)) 
        {
            Print("%s\r\n", lbuff);
            return(1);
        }
        else 
        {
            Print("?\r\n");
            return(0);
        }
    }
}


/* DIR PATH */
int do_ls(int agc, char **agv) 
{
    int fcount,iRet;
    char *ppath;
    
    long lTotal, lFree,lUsed;

    ppath = 0;

    if (agc == 1)
    {
        ppath = *agv;
    }
    else 
    {
        /* get the working dir of the default dir */
        if (pc_get_cwd((char *)shell_buf)) 
        {
            ppath = (char *)shell_buf;
            Print("path=[%s]\n\r",ppath);
            /* If not the root add a \\ */
            if (!(ppath[1] == '\\' && ppath[2] == '\0')) strcat(ppath, "\\");
            strcat(ppath, "*.*");
        }
    }
    if (ppath) 
    {
        /* These two lines added to make DIR A: display DIR A:*.* */
        if (ppath[1] == ':' && ppath[2] == '\0')
                strcat(ppath,"*.*");

        /* Now do the dir */
        fcount = pc_seedir(ppath);
        Print("%d Files(s)\n\r",fcount);

        /* And print the bytes remaining */
        lTotal=pc_get_totalSize_KB();
        lFree=pc_get_freeSize_KB();
        Print("total=%ld KB, free=%ld KB\n\r",lTotal,lFree);
        
    }
    else
          Print("Usage: DIR PATH, error code=%d\r\n",pc_get_errno());

    return(1);
}


/* List directory, and return number of matching file */
int pc_seedir(char *path) 
{ char *dirstr;
  int fcount = 0;
  DSTAT statobj;
  char sPath[80],sFullFileName[80];

    /* Get the first match */
    if (pc_gfirst(&statobj, path)) 
    {
        do
        {
            fcount++;

            Print("%-12s", statobj.filename);
            if (statobj.fattribute & AVOLUME)
            {
                Print("   <Volume> ");
                goto bypass;
            }
            else if (statobj.fattribute & ADIRENT)
                Print("  <SUB-DIR> ");
            else
            {
                sprintf(sFullFileName,"%s\\%s",statobj.path,statobj.filename);
                Print("%10ld ",pc_size(sFullFileName));
            }
            Print("%02d-%02d-%4d",(statobj.fdate>>5)&0xf,
                                  (statobj.fdate&0x1f),
                                  1980+((statobj.fdate>>9)&0xff)); /* Date */
            Print(" %02d:%02d",(statobj.ftime>>11)&0x1f,
                               (statobj.ftime>>5)&0x3f);    /* timr */
bypass:
            Print("  \r\n");
        }while(pc_gnext(&statobj));
        pc_gdone(&statobj); /* Call gdone to free up internal resources used by statobj */
    }
    return(fcount);
}



/* Set file attributes*/
int do_setattr(int agc, char **agv)                                    /*__fn__*/
{
    unsigned char attr;

    attr = 0;
    if (agc == 2)
    {
        if (!pc_get_attributes(*agv, &attr))
        {
            Print("Can't get attributes\r\n");
            return(0);
        }

        if (strcmp(*(agv+1),"RDONLY")==0)
                attr |= ARDONLY;
        else if (strcmp(*(agv+1),"HIDDEN")==0)
            attr |= AHIDDEN;
        else if (strcmp(*(agv+1),"SYSTEM")==0)
            attr |= ASYSTEM;
        else if (strcmp(*(agv+1),"ARCHIVE")==0)
            attr |= ARCHIVE;
        else if (strcmp(*(agv+1),"NORMAL")==0)
            attr =  ANORMAL;
        else
            goto usage;

        if (pc_set_attributes(*agv, attr))
            return(0);
          else
        {
                Print("Set attributes failed\r\n");
            return(0);
        }
    }
usage:
     Print("Usage: SETATTR D:Name RDONLY|HIDDEN|SYSTEM|ARCHIVE|NORMAL\r\n");

     return (0);
}


int do_format(int agc, char **agv)  
{
    return pc_format();    
}

int do_type(int agc, char **agv) 
{
    int fd,iRet;
    char buffer[101];
    if (agc == 1) 
    {
        fd=pc_open(*agv,(word)(PO_RDONLY),(word)(PS_IWRITE|PS_IREAD));
		if(fd< 0)
		{
			Print("pc_open error\n\r");
			return (1);
		}
		else 
		{
		    while(!pc_eof(fd))
		    {
		        iRet=pc_read(fd,buffer,100);
			    if (iRet>0)
			    {
			        buffer[iRet]=0;
				    Print("%s",buffer);
				}
				else
				{
				    Print("\n\r[pc_read error]\n\r");
				    return (1);
				}
		    }
		    return (0);
		}
    }
    Print("Usage: Type A:PATH\r\n");
    return (0);
}



int do_newCard(int agc, char **agv)
{
	int error;
    
    if (!pc_ertfs_init())
    {
	    Print("Initialize failed\n\r");
	    error=pc_get_errno();   
	    switch(error)
	    { 
	        case PCERR_BAD_FORMAT: //1
		        Print("Error 01: format is not FAT\n\r");
		        break;
		    case PCERR_NO_CARD: //2
		        Print("Error 02: no microSD card\n\r");
		        break;
		    default:
		        Print("Error %02d:\n\r",error);
		}
	}
    else
	{
        Print("Initialize ok.\n\r");
        Print("[Disk Size=%ld KB]\n\r",pc_get_totalSize_KB());
    }
    return 1;
}




void main(void)
{
    int error;
	char sDate[20];
	
	InitLib();
	
	pc_getLibDate(sDate);
	Print("=============================\n\r");
	Print("microSD libray version: %d [%s]\n\r\n\r",pc_getLibVer(),sDate);
	
	
	if(pc_init())
	{
        Print("Initialize ok.\n\r");
        Print("[Disk Size=%ld KB]\n\r",pc_get_totalSize_KB());
    }
    else
	{
	    Print("Initialize failed\n\r");
	    error=pc_get_errno();   
	    switch(error)
	    { 
	        case PCERR_BAD_FORMAT: //1
		        Print("Error 01: format is not FAT\n\r");
		        break;
		    case PCERR_NO_CARD: //2
		        Print("Error 02: no microSD card\n\r");
		        break;
		    default:
		        Print("Error %02d: unknow error\n\r",error);
		}
	}
	
	Print("=============================\n\r\n\r");
	
	DoCommand();
}
