/* Systeme de fichiers VFAT : fonctions sur les noms de fichiers. */
/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
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 3 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, see . */
#include "vfat_collide.h"
#include
#include
#include
#include
#include
/* Dit si un caractere OEM non nul est valide dans le jeu de caracteres des
* noms courts. */
static inline int isvalid_short(unsigned char c)
{
return isalnum(c) || c > 127 || strchr("$%'-_@~(){}^#&", c);
}
/* Dit si un caractere UNICODE non nul est valide dans le jeu de caracteres
* des noms longs. */
static inline int isvalid_long(unsigned short c)
{
return c > 255 || isvalid_short(c) || strchr(". +,;=[]", c);
}
/* Decode un nom court.
*
* shortname doit pointer vers une zone d'au moins 11 caracteres.
* buffer doit pointer vers une zone d'au moins 13 caracteres. */
static void decode_shortname(const unsigned char shortname[], unsigned char buffer[])
{
int n, l, i;
buffer[0] = shortname[0] == 0x05 ? 0xe5 : shortname[0]; /* 0xe5 est un caractere KANJI */
n = 1;
l = shortname[0] != ' ' ? 1 : 0;
i = 1;
while (i < 8)
if ((buffer[n++] = shortname[i++]) != ' ')
l = n;
if (shortname[8] != ' ') { /* extension */
buffer[n = l, n++] = '.';
while (i < 11)
if ((buffer[n++] = shortname[i++]) != ' ')
l = n;
}
buffer[l] = '\0';
}
/* Algorithme de generation de la base du nom court.
*
* lossy_conversion indique si des caracteres invalides ont du etre remplaces.
*/
static void generate_basisname(const unsigned short *longname, unsigned char shortname[], int *lossy_conversion)
{
unsigned char *p; /* portion courante (primaire ou extension) */
int n; /* nombre de caracteres copies dans la portion courante */
unsigned short c; /* caractere courant du nom long */
/* initialisation */
*lossy_conversion = 0;
/* conversion du nom long en majuscules (1) */
/* conversion des caracteres vers le jeu de caracteres 8.3 (2) */
/* suppression des espaces (3) */
/* suppression des points de tete (4) */
/* copie des caracteres dans la portion primaire du nom de base (5) */
/* copie des caracteres dans la portion de l'extension du nom de base (6, 7)
*/
p = shortname;
n = 0;
while ((c = toupper(*longname++))) { /* 1 */
if (c == ' ') /* 3 */
continue;
if (p == shortname) { /* portion primaire */
if (c == '.') {
if (n == 0) /* 4 */
continue;
while (n < 8) /* 5 */
p[n++] = ' ';
p += 8;
n = 0;
continue;
}
if (n == 8)
continue;
}
else { /* extension, 6, 7 */
if (c == '.') {
n = 0;
continue;
}
if (n == 3)
continue;
}
if (c > 255 || !isvalid_short(c)) { /* 2 */
c = '_';
*lossy_conversion = 1;
}
p[n++] = c;
}
while (&p[n] < shortname + 11)
p[n++] = ' ';
}
/* Dit si le nom long est identique au nom court, c'est-a-dire s'il respecte
* les conventions de nommage 8.3. */
static int fit(const unsigned short *longname, const unsigned char shortname[])
{
unsigned char buf[13];
int i;
decode_shortname(shortname, buf);
for (i = 0; buf[i]; i++)
if (*longname++ != buf[i])
return 0;
return !*longname;
}
/* Insere "~n" a la fin de la partie principale d'un nom court. */
static void insert_numerictail(unsigned char shortname[], unsigned int n)
{
unsigned char buf[8];
unsigned char *p;
int l;
buf[0] = '~';
l = 1 + snprintf((char *)(buf + 1), 7, "%u", n);
p = shortname;
while (*p != ' ' && p < shortname + 8 - l)
p++;
memcpy(p, buf, l);
}
/* Insere "~n" dans le nom court, de facon a ce qu'il n'y ait pas deux noms
* courts identiques dans le repertoire.
*
* lossy_conversion doit provenir de generate_basisname().
* Retourne 0 en cas de succes, un nombre negatif en cas d'erreur. */
static int generate_numerictail(const unsigned short *longname, unsigned char shortname[], int lossy_conversion, struct vfat_collide_fo_struct *collide_fo)
{
int n;
unsigned char primary[8];
int rv;
if (!lossy_conversion && fit(longname, shortname)) {
if ((rv = collide_fo->vptr->collide(collide_fo, shortname)) < 0)
return rv;
if (!rv)
return 0;
}
n = 1;
memcpy(primary, shortname, 8);
do {
if (n > 999999)
return -EINVAL;
memcpy(shortname, primary, 8);
insert_numerictail(shortname, n++);
if ((rv = collide_fo->vptr->collide(collide_fo, shortname)) < 0)
return rv;
}
while (rv);
return 0;
}
/* Genere le nom long d'un fichier.
*
* Retourne 0 en cas de succes, un nombre negatif en cas d'erreur (nom invalide). */
static int generate_longname(const char *fs_basename, unsigned short longname[])
{
int n; /* nombre de caracteres copies dans le nom long */
int spaces; /* nombre d'espaces et points consecutifs */
unsigned short c; /* caractere courant du nom */
n = 0;
spaces = 0;
while (c = get_fs_byte(fs_basename++), c && c != '/') {
if (c == ' ') {
if (n == 0) /* ignore leading spaces */
continue;
spaces++;
}
else if (c == '.')
/* leading periods are allowed */
spaces++;
else if (!isvalid_long(c))
return -EINVAL;
else
spaces = 0;
if (n > 255)
return -ENAMETOOLONG;
longname[n++] = c;
}
if (n > 255)
return -ENAMETOOLONG;
longname[n - spaces] = '\0'; /* ignore trailing spaces and periods */
return 0;
}
/* Genere le nom court d'un fichier.
*
* Retourne 0 en cas de succes, un nombre negatif en cas d'erreur. */
static int generate_shortname(const unsigned short *longname, unsigned char shortname[], struct vfat_collide_fo_struct *collide_fo)
{
int lossy_conversion;
generate_basisname(longname, shortname, &lossy_conversion);
return generate_numerictail(longname, shortname, lossy_conversion, collide_fo);
}
/* Dit si le nom de fichier concorde avec le nom long.
*
* basename doit pointer dans le segment fs. */
int vfat_match_long(const char *fs_basename, const unsigned short *longname)
{
unsigned short c;
while (*longname == ' ')
longname++;
while (c = get_fs_byte(fs_basename++), c && c != '/')
if (toupper(c) != toupper(*longname++))
return 0;
while (*longname == ' ' || *longname == '.')
longname++;
return !*longname;
}
/* Dit si le nom de fichier concorde avec le nom court.
*
* basename doit se trouver dans le segment pointe par fs. */
int vfat_match_short(const char *fs_basename, const unsigned char shortname[])
{
unsigned char buf[13];
int i;
decode_shortname(shortname, buf);
for (i = 0; buf[i]; i++)
if (toupper((unsigned char)get_fs_byte(fs_basename++)) != toupper(buf[i]))
return 0;
if (!strchr((char *)buf, '.') && get_fs_byte(fs_basename) == '.')
fs_basename++; /* le '.' est optionnel si le nom court n'a pas d'extension */
return !get_fs_byte(fs_basename) || get_fs_byte(fs_basename) == '/';
}
/* Decode un nom long.
*
* Conformement a la specification, les caracteres UNICODE non traduisibles
* sont remplaces par le caractere '_', et ce uniquement dans la reponse a
* l'utilisateur (pas de modifications sur le disque). */
void vfat_decode_longname(const unsigned short longname[], char name[])
{
int i;
i = 0;
do
name[i++] = *longname < 256 ? *longname : '_';
while (*longname++);
}
/* Decode un nom court. */
void vfat_decode_shortname(const unsigned char shortname[], char name[])
{
decode_shortname(shortname, (unsigned char *)name);
}
/* Genere les noms long et court d'un fichier.
*
* basename doit pointer dans le segment fs.
* longname doit pointer vers une zone d'au moins 256 caracteres.
* shortname doit pointer vers une zone d'au moins 11 caracteres.
* Retourne 0 en cas de succes, un nombre negatif en cas d'erreur. */
int vfat_generate_names(const char *fs_basename, unsigned short longname[], unsigned char shortname[], struct vfat_collide_fo_struct *collide_fo)
{
int rv;
if ((rv = generate_longname(fs_basename, longname)) < 0)
return rv;
if ((rv = generate_shortname(longname, shortname, collide_fo)) < 0)
return rv;
return 0;
}
/* Dit si le nom long est identique au nom court. Si oui, il n'est pas
* necessaire de creer une entree de repertoire pour le nom long. */
int vfat_fit(const unsigned short *longname, const unsigned char shortname[])
{
return fit(longname, shortname);
}