View of xos/fs/vfat_names.c


XOS | Parent Directory | View | Download

/* 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 <http://www.gnu.org/licenses/>. */
 
#include "vfat_collide.h"
 
#include <segment.h>
#include <errno.h>
#include <sprintf.h>
#include <string.h>
#include <ctype.h>
 
/* 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);
}