A UNIX emulation shell to access the underlying SDCard FileSystem through a terminal interface

Dependents:   Waldo_Embed_V2

Information

SDShell does not change the com baudrate. Access is made using the baud as initialized by the Serial object when passed into the SDShell object

Example

#include "mbed.h"
#include "SDFileSystem.h"
#include "SDShell.h"

Serial com(USBTX, USBRX);
SDFileSystem sd(p11, p12, p13, p14, "sd");
SDShell emulate;

int main()
{
    emulate.init();
    emulate.shell(com, sd, "/sd");
}

SDShell.cpp

Committer:
sam_grove
Date:
2013-05-27
Revision:
13:6c520cfc50b2
Parent:
12:8dbe4f4c1216

File content as of revision 13:6c520cfc50b2:


/**
 * @file    SDShell.cpp
 * @brief   SD Card Utility - Emulate a basic UNIX terminal interface
 * @author  sam grove
 * @version 1.0
 * @see     
 *
 * Copyright (c) 2013
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#include "SDShell.h"

SDShell::SDShell()
{
    _debug = 0;
    memset(_cwd, 0, SHELL_BUF_SIZE);

    return;
}

void SDShell::init(void)
{
    // add known commands to a linked list
    _cmds.attachMsg("ls"   , this, &SDShell::ls);
    _cmds.attachMsg("cd"   , this, &SDShell::cd);
    _cmds.attachMsg("pwd"  , this, &SDShell::pwd);
    _cmds.attachMsg("head" , this, &SDShell::head);
    _cmds.attachMsg("cat"  , this, &SDShell::cat);
    _cmds.attachMsg("bcat" , this, &SDShell::bcat);
    _cmds.attachMsg("mkdir", this, &SDShell::create);
    _cmds.attachMsg("touch", this, &SDShell::touch);
    _cmds.attachMsg("rm"   , this, &SDShell::rm);
    _cmds.attachMsg("exit" , this, &SDShell::exit);
    _cmds.attachMsg("debug", this, &SDShell::debug);
    _cmds.attachMsg("du"   , this, &SDShell::du);
    _cmds.attachMsg("cksum", this, &SDShell::cksum);
    
    return;
}

void SDShell::shell(Serial &com, SDFileSystem &storage, char const *cwd)
{
    uint32_t done = 0;

    // get local copies of the initialized objects
    _com = &com;
    _storage = &storage;

    // put the current working directory to the root of the card - should be pulled in I think
    strcpy(_cwd, cwd);

    while(0 == done) 
    {
        memset(_cmd    , 0, SHELL_BUF_SIZE);
        memset(_cmdline, 0, SHELL_BUF_SIZE);
        memset(_newpath, 0, SHELL_BUF_SIZE);
        // gather input from the Serial object
        shellInput();
        // break up the command line arguemnt
        _arg = split(_cmd, _cmdline, 64, ' ');
        // look at the arg and get paths and files separated if present
        resolveDirectory(_newpath, _arg);
        // print parsed members if we're debugging
        if(_debug)
        {
            LOG("cmdline:<%s> cmd:<%s> arg:<%s> newpath:<%s>\n", _cmdline, _cmd, _arg, _newpath);
        }
        // now service known messages
        char* result = _cmds.serviceMessage(_cmd);
        // look at the result > 0 means we found somehting
        if(result == (char *)EXIT)
        {
            done = 1;   // force an exit
        }
        else if (result == (char *)UNKNOWN)    // didnt know what that was
        {
            uint32_t cnt = 1;
            LOG("Unknown Message from Terminal: Options are\n");
            do{
                result = _cmds.messageLookup(cnt++);
                _com->printf(" %s\n", result);
            } while(result != NULL);
        }
        else
        {
            // that should have done something
        }
    }
}

void SDShell::shellInput(void)
{
    int i=0;
    char c;
    uint32_t done = 0;
    // clear the last command
//    memset(_cmdline, 0, SHELL_BUF_SIZE);
    _com->printf("# ", _cwd);
    do
    {
        _cmdline[i] = 0;    // clearing the next loc before using it is faster than memset
        c = _com->getc();   // get a char
        if ((c == '\r') || (c == '\n'))      // process on "enter"
        {
            done = 1;
        } 
        else if (i < SHELL_BUF_MASK)    // once full the user can only press enter
        {
            if (c == 0x7f)  // backspace
            {
                if (i > 0)  // if we're at the beginning, do nothing
                {
                    i--;
                    _com->printf("\b \b");
                }
            }
            else    // valid keystrokes get stored and echo'd
            {
                _com->putc(c);
                _cmdline[i++] = c;
            }
        }
    } while(0 == done);

    _com->printf("\n");
}

char *SDShell::split(char *dst, char *src, int max, char delim)
{
    int i = 0;
    char *v;

    // make sure pointers are valid (could validate RAM but that should be the caller responsibility)
    if ((dst == 0) || (src == 0)) 
    {
        return 0;
    }
    // break up the string until delim is found
    while((*src != 0) && (*src != delim) && (i < max)) 
    {
        *(dst++) = *(src++);
        i++;
    }
    // return what comes after the delimiter - dst has before the delimiter
    *dst = 0;
    v = (*src == '\0') ? dst : (src+1);

    return v;
}

void SDShell::resolveDirectory(char *newpath, char *path)
{
    char basename[64], dirname[64];

    // absolute path
    if (path[0] == '/') 
    {
        strcpy(newpath, path);
    }
    // relative path
    else 
    {
        strcpy(newpath, _cwd);
        // make sure something was passed
        if(path[0] != 0) 
        {
            // add the backslash if the user didnt
            if(newpath[strlen(newpath)-1] != '/') 
            {
                strcat(newpath, "/");
            }
            strcat(newpath, path);
        }
        // Resolve .. references
        splitName(newpath, dirname, basename);
        if (0 == strcmp(basename, "..")) 
        {
            splitName(dirname, newpath, basename);
        }
    }
    
    return;
}

void SDShell::splitName(char *path, char *dirname, char *basename)
{
    int sep = 0;
    // print the original path
    if (_debug)
    {
        LOG("%d\n", strlen(path));
    }
    // find the directory backslash location in the path
    for (int i=strlen(path)-1; i >= 0; i--) 
    {
        if (_debug) // print what we found
        {
            LOG("- %c\n", path[i]);
        }
        sep = i;
        if (path[i] == '/')
        {
            break;
        }
    }
    // extract the directory
    for (int i=0; i < sep; i++)
    {
        if (_debug) // print what we found
        {
            LOG("> %c\n", path[i]);
        }
        dirname[i] = path[i];
        dirname[i+1] = 0;
    }
    // and then split the file from directory
    for (int i=sep+1; i < strlen(path); i++) 
    {
        if (_debug) // print what we found
        {
            LOG("* %c\n", path[i]);
        }
        basename[i-(sep+1)] = path[i];
        basename[i-sep] = 0;
    }
    if (_debug) // print the the split
    {
        LOG("d:<%s> b:<%s>\n", dirname, basename);
    }
}

char *SDShell::ls(char *cmd)
{
    if (_debug)
    {
        LOG("%s\n", _cwd);
    }
    
    DIR *d = opendir(_newpath);
    if (NULL != d)
    {
        struct dirent *p;
        while ((p = readdir(d)) != NULL)
        {
            _com->printf(" %s\n", p->d_name);
        }
        closedir(d);
    }
    else
    {
        _com->printf("%s: No such directory\n", _newpath);
    }
    
    return (char *)OK;
}

char *SDShell::cd(char *cmd)
{
    strcpy(_cwd, _newpath);
    
    return (char *)OK;
}

char *SDShell::pwd(char *path) 
{
    _com->printf("%s\n", _cwd);
    
    return (char *)OK;
}

char *SDShell::head(char *cmd)
{
    FILE *fp = fopen(_newpath, "r");
    if (fp != NULL) 
    {
        uint32_t line = 0;
        while ((0 == feof(fp)) && ((line++) < 10))
        {
            fgets(_buf, 512, fp);
            _com->printf("%s", _buf);
        }
        fclose(fp);
    } 
    else
    {
        _com->printf("%s: No such file\n", _newpath);
    }
    
    return (char *)OK;
}

char *SDShell::cat(char *cmd)
{
    memset(_buf, 0, 512);
    FILE *fp= fopen(_newpath, "r");
    if (fp != NULL)
    {
        while (!feof(fp))
        {
            fread(_buf, 1, 512, fp);
            _com->printf("%s", _buf);
        }
        fclose(fp);
    }
    else
    {
        _com->printf("%s: No such file\n", _newpath);
    }
    
    return (char *)OK;
}

char *SDShell::bcat(char *cmd)
{
    //uint8_t buf[4] = {NULL};
    memset(_buf, 0, 512);
    FILE *fp= fopen(_newpath, "rb");
    if (fp != NULL)
    {
        while (!feof(fp))
        {
            //fread(buf, 1, 1, fp);
            //_com->putc(buf[0]);
            fread(_buf, 1, 512, fp);
            //_com->write(_buf, 512);
            for(int i=0; i<512; i++)
            {
                _com->putc(_buf[i]);
            }
        }
        fclose(fp);
    }
    else
    {
        _com->printf("%s: No such file\n", _newpath);
    }
    return (char *)OK;
}

char *SDShell::touch(char *cmd)
{
    FILE *fp = fopen(_newpath, "w");
    if (fp != NULL)
    {
        _com->printf("%s: File created\n", _newpath);
        fclose(fp);
    }
    else
    {
        _com->printf("%s: No such file\n", _newpath);
    }
    
    return (char *)OK;
}

char *SDShell::create(char *cmd)
{
    mkdir(_newpath, 1023);
    
    return (char *)OK;
}

char *SDShell::rm(char *cmd)
{
    remove(_newpath);
    
    return (char *)OK;
}

char *SDShell::exit(char *cmd)
{
    return (char *)EXIT;
}

char *SDShell::debug(char *cmd)
{
    _debug = !_debug;
    
    return (char *)OK;
}

char *SDShell::du(char *cmd)
{
    uint32_t file_size = 0;
    memset(_buf, 0, 512);
    
    FILE *fp= fopen(_newpath, "rb");
    if (fp != NULL)
    {
        fseek(fp, 0L, SEEK_END);
        file_size = ftell(fp);
        fseek(fp, 0L, SEEK_SET);
        fclose(fp);
        _com->printf("%d %s\n", file_size, _newpath);
    }
    else
    {
        _com->printf("%s: No such file\n", _newpath);
    }
    return (char *)OK;
}

char *SDShell::cksum(char *cmd)
{
    uint32_t file_size = 0;
    unsigned long crc = 0;
    memset(_buf, 0, 512);
    
    FILE *fp= fopen(_newpath, "rb");
    if (fp != NULL)
    {
        while (!feof(fp))
        {
            uint32_t tmp = fread(_buf, 1, 512, fp);
            file_size += tmp;   // add up the file size in bytes
            for(int i=0; i<tmp; i++)
            {
                CRC32Value(crc, _buf[i]);   // build the crc
            }
        }
        fclose(fp);
        _com->printf("%u %d %s\n", crc, file_size, _newpath);
    }
    else
    {
        _com->printf("%s: No such file\n", _newpath);
    }
    return (char *)OK;
}