Newer
Older
barebox / fs / fdos / vfat.c
/*
 * (C) Copyright 2002
 * Stäubli Faverges - <www.staubli.com>
 * Pierre AUBERT  p.aubert@staubli.com
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <config.h>

#if (CONFIG_COMMANDS & CFG_CMD_FDOS)
#include <linux/ctype.h>

#include "dos.h"
#include "fdos.h"

static int dir_read (Fs_t *fs,
                     Slot_t *dir,
                     Directory_t *dirent,
                     int num,
                     struct vfat_state *v);

static int unicode_read (char *in, char *out, int num);
static int match (const char *s, const char *p);
static unsigned char sum_shortname (char *name);
static int check_vfat (struct vfat_state *v, Directory_t *dir);
static char *conv_name (char *name, char *ext, char Case, char *ans);


/*-----------------------------------------------------------------------------
 * clear_vfat -- 
 *-----------------------------------------------------------------------------
 */
static void clear_vfat (struct vfat_state *v)
{
    v -> subentries = 0;
    v -> status = 0;
}

/*-----------------------------------------------------------------------------
 * vfat_lookup -- 
 *-----------------------------------------------------------------------------
 */
int vfat_lookup (Slot_t *dir,
                 Fs_t *fs,
                 Directory_t *dirent,
                 int *entry,
                 int *vfat_start,
                 char *filename, 
                 int flags,
                 char *outname,
                 Slot_t *file)
{
    int found;
    struct vfat_state vfat;
    char newfile [VSE_NAMELEN];
    int vfat_present = 0;

    if (*entry == -1) {
        return -1;
    }

    found = 0;
    clear_vfat (&vfat);
    while (1) {
        if (dir_read (fs, dir, dirent, *entry, &vfat) < 0) {
            if (vfat_start) {
                *vfat_start = *entry;
            }
            break;
        }
        (*entry)++;

        /* Empty slot                                                        */
        if (dirent -> name[0] == '\0'){
            if (vfat_start == 0) {
                break;
            }
            continue;
        }

        if (dirent -> attr == ATTR_VSE) {
            /* VSE entry, continue                                           */
            continue;
        }
        if ( (dirent -> name [0] == DELMARK) ||
             ((dirent -> attr & ATTR_DIRECTORY) != 0 &&
              (flags & ACCEPT_DIR) == 0) ||
             ((dirent -> attr & ATTR_VOLUME) != 0 &&
              (flags & ACCEPT_LABEL) == 0) ||
             (((dirent -> attr & (ATTR_DIRECTORY | ATTR_VOLUME)) == 0) &&
              (flags & ACCEPT_PLAIN) == 0)) {
            clear_vfat (&vfat);
            continue;
        }

        vfat_present = check_vfat (&vfat, dirent);
        if (vfat_start) {
            *vfat_start = *entry - 1;
            if (vfat_present) {
                *vfat_start -= vfat.subentries;
            }
        }

        if (dirent -> attr & ATTR_VOLUME) {
            strncpy (newfile, dirent -> name, 8);
            newfile [8] = '\0';
            strncat (newfile, dirent -> ext, 3);
            newfile [11] = '\0';
        }
        else {
            conv_name (dirent -> name, dirent -> ext, dirent -> Case, newfile);
        }

        if (flags & MATCH_ANY) {
            found = 1;
            break;
        }

        if ((vfat_present && match (vfat.name, filename)) ||
            (match (newfile, filename))) {
            found = 1;
            break;
        }
        clear_vfat (&vfat);
    }

    if (found) {
        if ((flags & DO_OPEN) && file) {
            if (open_file (file, dirent) < 0) {
                return (-1);
            }
        }
        if (outname) {
            if (vfat_present) {
                strcpy (outname, vfat.name);
            }
            else {
                strcpy (outname, newfile);
            }
        }
        return (0);                    /* File found                         */
    } else {
        *entry = -1;
        return -1;                      /* File not found                    */
    }
}

/*-----------------------------------------------------------------------------
 * dir_read -- Read one directory entry
 *-----------------------------------------------------------------------------
 */
static int dir_read (Fs_t *fs,
              Slot_t *dir,
              Directory_t *dirent,
              int num,
              struct vfat_state *v)
{

    /* read the directory entry                                              */
    if (read_file (fs,
                   dir,
                   (char *)dirent,
                   num * MDIR_SIZE,
                   MDIR_SIZE) != MDIR_SIZE) {
        return (-1);
    }

    if (v && (dirent -> attr == ATTR_VSE)) {
        struct vfat_subentry *vse;
        unsigned char id, last_flag;
        char *c;

        vse = (struct vfat_subentry *) dirent;
        id = vse -> id & VSE_MASK;
        last_flag = (vse -> id & VSE_LAST);
        if (id > MAX_VFAT_SUBENTRIES) {
            /* Invalid VSE entry                                             */
            return (-1);
        }


        /* Decode VSE                                                        */
        if(v -> sum != vse -> sum) {
            clear_vfat (v);
            v -> sum = vse -> sum;
        }

        
        v -> status |= 1 << (id - 1);
        if (last_flag) {
            v -> subentries = id;
        }

        c = &(v -> name [VSE_NAMELEN * (id - 1)]);
        c += unicode_read (vse->text1, c, VSE1SIZE);
        c += unicode_read (vse->text2, c, VSE2SIZE);
        c += unicode_read (vse->text3, c, VSE3SIZE);
		
        if (last_flag) {
            *c = '\0';	        /* Null terminate long name                  */
        }
        
    }
    return (0);
}

/*-----------------------------------------------------------------------------
 * unicode_read -- 
 *-----------------------------------------------------------------------------
 */
static int unicode_read (char *in, char *out, int num)
{
    int j;
    
    for (j = 0; j < num; ++j) {
        if (in [1])
            *out = '_';
        else
            *out = in [0];
        out ++;
        in += 2;
    }
    return num;
}

/*-----------------------------------------------------------------------------
 * match -- 
 *-----------------------------------------------------------------------------
 */
static int match (const char *s, const char *p)
{

    for (; *p != '\0'; ) {
        if (toupper (*s) != toupper (*p)) {
            return (0);
        }
        p++;
        s++;
    }
    
    if (*s != '\0') {
        return (0);
    }
    else {
        return (1);
    }
}
/*-----------------------------------------------------------------------------
 * sum_shortname -- 
 *-----------------------------------------------------------------------------
 */
static unsigned char sum_shortname (char *name)
{
    unsigned char sum;
    int j;
    
    for (j = sum = 0; j < 11; ++j) {
        sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) +
            (name [j] ? name [j] : ' ');
    }
    return (sum);
}
/*-----------------------------------------------------------------------------
 * check_vfat -- 
 * Return 1 if long name is valid, 0 else
 *-----------------------------------------------------------------------------
 */
static int check_vfat (struct vfat_state *v, Directory_t *dir)
{
    char name[12];
    
    if (v -> subentries == 0) {
        return 0;
    }
    
    strncpy (name, dir -> name, 8);
    strncpy (name + 8, dir -> ext, 3);
    name [11] = '\0';
    
    if (v -> sum != sum_shortname (name)) {
        return 0;
    }
    
    if( (v -> status & ((1 << v -> subentries) - 1)) !=
        (1 << v -> subentries) - 1) {
        return 0;
    }
    v->name [VSE_NAMELEN * v -> subentries] = 0;
    
    return 1;
}
/*-----------------------------------------------------------------------------
 * conv_name -- 
 *-----------------------------------------------------------------------------
 */
static char *conv_name (char *name, char *ext, char Case, char *ans)
{
    char tname [9], text [4];
    int i;

    i = 0;
    while (i < 8 && name [i] != ' ' && name [i] != '\0') {
        tname [i] = name [i];
        i++;
    }
    tname [i] = '\0';
    
    if (Case & BASECASE) {
        for (i = 0; i < 8 && tname [i]; i++) {
            tname [i] = tolower (tname [i]);
        }
    }

    i = 0;
    while (i < 3 && ext [i] != ' ' && ext [i] != '\0') {
        text [i] = ext [i];
        i++;
    }
    text [i] = '\0';

    if (Case & EXTCASE){
        for (i = 0; i < 3 && text [i]; i++) {
            text [i] = tolower (text [i]);
        }
    }

    if (*text) {
        strcpy (ans, tname);
        strcat (ans, ".");
        strcat (ans, text);
    }
    else {
        strcpy(ans, tname);
    }
    return (ans);
}


#endif