Scans the folder structure.

Dependents:   GR-PEACH_Digital_Signage

scan_folder.cpp

Committer:
dkato
Date:
2016-05-31
Revision:
0:c72b82f46360

File content as of revision 0:c72b82f46360:

/*******************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only
* intended for use with Renesas products. No other uses are authorized. This
* software is owned by Renesas Electronics Corporation and is protected under
* all applicable laws, including copyright laws.
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
* LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
* TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
* ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
* ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software
* and to discontinue the availability of this software. By using this software,
* you agree to the additional terms and conditions found by accessing the
* following link:
* http://www.renesas.com/disclaimer*
* Copyright (C) 2015 Renesas Electronics Corporation. All rights reserved.
*******************************************************************************/

#include "mbed.h"
#include "rtos.h"
#include "FATFileSystem.h"
#include "scan_folder.h"

/*--- Macro definition of folder structure scan. ---*/
/* The character string to identify root directory. */
#define STR_ROOT_FOR_F_OPENDIR  ""                      /* to use f_opendir() */

#define CHR_FULL_STOP           '.'         /* 0x2E: FULL STOP */
#define CHR_SOLIDUS             '/'         /* 0x2F: SOLIDUS */
#define FOLD_ID_NOT_EXIST       (0xFFFFFFFFu)
#define OPEN_MODE_READ_ONLY     "r"

/* File path maximum size including the usb mount name size */
#define FILE_PATH_MAX_SIZE      (64u)

#define SCAN_WAIT_TIME_MS       (1)


void ScanFolder::init() {
    scan_data.total_folder = 0u;
    scan_data.total_file = 0u;
}

bool ScanFolder::scan(char_t * p_root_name, const char_t ** pp_extension_tbl) {
    bool            ret = false;
    bool            result;
    bool            chk;
    uint32_t        i;
    item_t          *p_item;
    FATFS_DIR       fdir;
    const char_t    *p_name;
    bool            flg_dir;
    bool            chk_dep;

    /* Initializes the scan data. */
    scan_data.total_file = 0u;
    scan_data.total_folder = 0u;

    /* Registers the identifier of the root directory to use f_opendir(). */
    (void) regist_item(&scan_data.folder_list[0], STR_ROOT_FOR_F_OPENDIR, FOLD_ID_NOT_EXIST);
    scan_data.total_folder++;

    /* Checks the item in all registered directory. */
    for (i = 0; i < scan_data.total_folder; i++) {
        chk_dep = check_folder_depth(i);
        result = open_dir(&scan_data.folder_list[i], &fdir);
        while (result != false) {
            result = read_dir(&fdir, &p_name, &flg_dir);
            if (result != false) {
                /* Checks the attribute of this item. */
                if (flg_dir != false) {
                    /* This item is directory. */
                    if ((chk_dep != false) && (scan_data.total_folder < MAX_FOLDER_NUM)) {
                        p_item = &scan_data.folder_list[scan_data.total_folder];
                        chk = regist_item(p_item, p_name, i);
                        if (chk != false) {
                            /* Register of directory item was success. */
                            scan_data.total_folder++;
                        }
                    }
                } else {
                    /* This item is file. */
                    chk = check_extension(p_name, pp_extension_tbl);
                    if ((chk != false) && (scan_data.total_file < MAX_TRACK_NUM)) {
                        /* This item is requested file. */
                        p_item = &scan_data.track_list[scan_data.total_file];
                        chk = regist_item(p_item, p_name, i);
                        if (chk != false) {
                            /* Register of file item was success. */
                            scan_data.total_file++;
                        }
                    }
                }
                Thread::wait(SCAN_WAIT_TIME_MS);
            }
        }
    }
    /* Changes the identifier of the root directory to use fopen(). */
    (void) regist_item(&scan_data.folder_list[0], p_root_name, FOLD_ID_NOT_EXIST);

    if (scan_data.total_file > 0u) {
        ret = true;
    }

    return ret;
}

FILE * ScanFolder::open(const uint32_t track_id) {
    FILE            *fp = NULL;
    const char_t    *p_path;
    size_t          path_len;

    if (track_id < scan_data.total_file) {
        p_path = get_full_path(&scan_data.track_list[track_id]);
        if (p_path != NULL) {
            path_len = strlen(p_path); 
            /* File path maximum length is limited by the specification of "fopen". */
            if (path_len < FILE_PATH_MAX_SIZE) {
                fp = fopen(p_path, OPEN_MODE_READ_ONLY);
            }
        }
    }
    return fp;
}

void ScanFolder::close(FILE * const fp) {
    if (fp != NULL) {
        (void) fclose(fp);
    }
}

const char_t * ScanFolder::getFileName(const uint32_t track_id) {
    const char_t    *p_name = NULL;

    if (track_id < scan_data.total_file) {
        p_name = &scan_data.track_list[track_id].name[0];
    }
    return p_name;
}

uint32_t ScanFolder::getTotalFile() {
    uint32_t        ret;

    ret = scan_data.total_file;
    return ret;
}

/** Gets the full path
 *
 *  @param p_item Pointer to the item structure of the folder / track.
 *
 *  @returns 
 *    Pointer to the full path.
 */
const char_t * ScanFolder::get_full_path(const item_t * const p_item) {
    const char_t    *p_path = NULL;
    const item_t    *p;
    const item_t    *item_list[MAX_FOLDER_DEPTH];
    uint32_t        i;
    uint32_t        item_cnt;
    uint32_t        buf_cnt;
    uint32_t        len;
    bool            err;

    if (p_item != NULL) {
        for (i = 0; i < MAX_FOLDER_DEPTH; i++) {
            item_list[i] = NULL;
        }
        scan_data.work_buf[0] = '\0';

        /* Stores the item name until the root folder. */
        p = p_item;
        item_cnt = 0;
        while ((item_cnt < MAX_FOLDER_DEPTH) && 
               (p->parent_number < scan_data.total_folder)) {
            item_list[item_cnt] = p;
            item_cnt++;
            p = &scan_data.folder_list[p->parent_number];
        } 
        if (p->parent_number == FOLD_ID_NOT_EXIST) {
            buf_cnt = strlen(p->name);
            (void) strncpy(&scan_data.work_buf[0], p->name, sizeof(scan_data.work_buf));
            err = false;
            while ((item_cnt > 0u) && (err != true)) {
                item_cnt--;
                p = item_list[item_cnt];
                /* Concatenates SOLIDUS character to the "work_buf" variable. */
                if ((buf_cnt + 1u) < sizeof(scan_data.work_buf)) {
                    scan_data.work_buf[buf_cnt] = CHR_SOLIDUS;
                    buf_cnt++;
                } else {
                    err = true;
                }
                /* Concatenates the item name to the "work_buf" variable. */
                if (p != NULL) {
                    len = strlen(p->name);
                    if ((buf_cnt + len) < sizeof(scan_data.work_buf)) {
                        (void) strncpy(&scan_data.work_buf[buf_cnt], p->name, len);
                        buf_cnt += len;
                    } else {
                        err = true;
                    }
                }
            }
            if (err != true) {
                scan_data.work_buf[buf_cnt] = '\0';
                p_path = &scan_data.work_buf[0];
            }
        }
    }
    return p_path;
}

/** Opens the directory
 *
 *  @param p_item Pointer to the item structure of the folder / track.
 *  @param p_fdir Pointer to the structure to store the directory object.
 *
 *  @returns 
 *    Results of process. true is success. false is failure.
 */
bool ScanFolder::open_dir(const item_t * const p_item, FATFS_DIR * const p_fdir) {
    bool            ret = false;
    const char_t    *p_path;
    FRESULT         ferr;

    if ((p_item != NULL) && (p_fdir != NULL)) {
        p_path = get_full_path(p_item);
        if (p_path != NULL) {
            ferr = f_opendir(p_fdir, p_path);
            if (ferr == FR_OK) {
                ret = true;
            }
        }
    }
    return ret;
}

/** Reads the directory
 *
 *  @param p_fdir Pointer to the structure of the directory object.
 *  @param p_flag_dir Pointer to the variable to store the directory flag.
 *
 *  @returns 
 *    Pointer to the name.
 */
bool ScanFolder::read_dir(FATFS_DIR * const p_fdir,  const char_t ** const p_name, bool * const p_flag_dir) {
    bool            ret = false;
    FRESULT         ferr;
    FILINFO         finfo;

    if ((p_fdir != NULL) && 
        (p_name != NULL) && (p_flag_dir != NULL)) {
        /* Sets the buffer to store the long file name. */
        finfo.lfname = &scan_data.work_buf[0];
        finfo.lfsize = sizeof(scan_data.work_buf);
        ferr = f_readdir(p_fdir, &finfo);
        if ((ferr == FR_OK) && ((int32_t)finfo.fname[0] != '\0')) {
            if (finfo.lfname != NULL) {
                if ((int32_t)finfo.lfname[0] == '\0') {
                    /* Long file name does not exist. */
                    (void) strncpy(finfo.lfname, finfo.fname, finfo.lfsize);
                }
                /* Adds the NULL terminal character. */
                /* This is fail-safe processing. */
                finfo.lfname[finfo.lfsize - 1u] = '\0';

                ret = true;
                *p_name = finfo.lfname;
                if ((finfo.fattrib & AM_DIR) != 0) {
                    /* This item is directory. */
                    *p_flag_dir = true;
                } else {
                    /* This item is file. */
                    *p_flag_dir = false;
                }
            }
        }
    }
    return ret;
}

/** Registers the item of the folder / track
 *
 *  @param p_item Pointer to the structure to store the item of the folder / track.
 *  @param p_name Pointer to the name of the item.
 *  @param parent Number of the parent folder of the item.
 *
 *  @returns 
 *    Results of process. true is success. false is failure.
 */
bool ScanFolder::regist_item(item_t * const p_item, const char_t * const p_name, const uint32_t parent) {
    bool        ret = false;
    uint32_t    len;

    if ((p_item != NULL) && (p_name != NULL)) {
        len = strlen(p_name);
        if ((len + 1u) < sizeof(p_item->name)) {
            (void) strncpy(p_item->name, p_name, sizeof(p_item->name));
            p_item->name[sizeof(p_item->name) - 1u] = '\0';
            p_item->parent_number = parent;
            ret = true;
        }
    }
    return ret;
}

/** Checks the extension of the track name
 *
 *  @param p_name Pointer to the name of the track.
 *
 *  @returns 
 *    Results of the checking. true is requested file. false is other file.
 */
bool ScanFolder::check_extension(const char_t * const p_name, const char_t ** pp_extension_tbl) {
    bool            ret = false;
    const char_t    *p;
    size_t          extension_len;
    const char_t *  p_extension;

    if (p_name != NULL) {
        p = strrchr(p_name, CHR_FULL_STOP);
        if (p != NULL) {
            while (1) {
                p_extension = *(pp_extension_tbl++);
                if (p_extension != NULL) {
                    extension_len = strlen(p_extension);
                    if (strncasecmp(p, p_extension, extension_len) == 0) {
                        ret = true;
                        break;
                    }
                } else {
                    break;
                }
            }
        }
    }
    return ret;
}

/** Checks the folder depth in the scan range
 *
 *  @param folder_id Folder ID [0 - (total folder - 1)]
 *
 *  @returns 
 *    Results of the checking. true is the scan range. false is out of a scan range.
 */
bool ScanFolder::check_folder_depth(const uint32_t folder_id) {
    bool            ret = false;
    uint32_t        depth;
    uint32_t        parent_id;

    /* Counts the folder depth. */
    parent_id = folder_id;
    depth = 0u;
    while ((depth < MAX_FOLDER_DEPTH) && 
           (parent_id < scan_data.total_folder)) {
        depth++;
        parent_id = scan_data.folder_list[parent_id].parent_number;
    } 
    if (parent_id == FOLD_ID_NOT_EXIST) {
        /* Found the root folder. */
        if (depth < MAX_FOLDER_DEPTH) {
            ret = true;
        }
    }
    return ret;
}