View of xos/tools/names.c


XOS | Parent Directory | View | Download

/* 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 "names.h"
 
#include "file_info.h"
 
#include <string.h>
#include <ctype.h>
 
#define ustrchr(s, c)   (unsigned char *)strchr((const char *)s, c)
#define ustrrchr(s, c)  (unsigned char *)strrchr((const char *)s, c)
 
/* Dit si un caractere est valide dans le jeu de caracteres des noms courts. */
static inline int isvalid(unsigned char c)
{
  return isalnum(c) || c > 127 || (c && strchr("$%'-_@~(){}^#&", c));
}
 
/* Decode un nom court a partir de sa representation en memoire.
 *
 * buffer doit pointer sur une zone d'au moins 13 octets. */
static void decode(const unsigned char shortname[], unsigned char buffer[])
{
  unsigned char *pd;
 
  pd = buffer;
  memcpy(pd, shortname, 8);
  pd[8] = ' ';
  pd = ustrchr(pd, ' ');
  if (shortname[8] != ' ') {
    *pd++ = '.';
    memcpy(pd, shortname + 8, 3);
    pd[3] = ' ';
    pd = ustrchr(pd, ' ');
  }
  *pd = '\0';
}
 
/* Dit si un nom long coincide avec le nom court specifie, en tenant compte de
 * la casse. */
static int fit(const unsigned short *longname, const unsigned char shortname[])
{
  unsigned char buffer[13];
  unsigned char *pb;
 
  decode(shortname, pb = buffer);
  do {
    if (*pb != *longname)
      return 0;
    if (!*pb)
      return 1;
    pb++;
    longname++;
  }
  while (1);
}
 
/* Dit si un nom long coincide avec le nom court specifie, sans tenir compte de
 * la casse. */
static int ifit(const unsigned short *longname, const unsigned char shortname[])
{
  unsigned char buffer[13];
  unsigned char *pb;
 
  decode(shortname, pb = buffer);
  while (*pb && *longname && toupper(*pb++) == toupper(*longname++));
  return !*pb && !*longname;
}
 
/* Dit si le dernier nom court d'un repertoire coincide avec un nom court deja
 * present dans le repertoire. */
static int collide(const struct file_info_struct *dir_info)
{
  unsigned int i;
 
  for (i = 0; i < dir_info->count - 1; i++)
    if (!memcmp(dir_info->entries[dir_info->count - 1].shortname,
                dir_info->entries[i].shortname, 11))
      return 1;
  return 0;
}
 
/* Insere "~n" a la fin de la base d'un nom court. */
static void insert_numerictail(unsigned char shortname[], unsigned int n)
{
  unsigned char buffer[8];
  unsigned char *pd;
  unsigned int i;
 
  for (i = 7; n && i > 0; i--) {
    buffer[i] = (n % 10) + '0';
    n /= 10;
  }
  buffer[i] = '~';
  pd = shortname;
  while (*pd != ' ' && pd < shortname + i)
    pd++;
  memcpy(pd, buffer + i, 8 - i);
}
 
/* Genere un nom court a partir d'un nom long.
 *
 * shortname doit pointer sur une zone d'au moins 11 octets.
 * 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 buffer[256];
  const unsigned short *pss;
  const unsigned char *ps;
  unsigned char *pd;
  unsigned int i;
 
  *lossy_conversion = 0;
  memset(shortname, ' ', 11);
  pss = longname; /* 1 */
  pd = buffer;
  do
    *pd++ = toupper((unsigned char)*pss);
  while (*pss++ && (size_t)(pd - buffer) < sizeof buffer - 1);
  buffer[sizeof buffer - 1] = '\0';
  for (pd = buffer; *pd; pd++) /* 2 */
    if (!isvalid(*pd) && *pd != ' ' && *pd != '.') {
      *pd = '_';
      *lossy_conversion = 1;
    }
  ps = pd = buffer; /* 3 */
  do
    if (*ps != ' ')
      *pd++ = *ps;
  while (*ps++);
  ps = pd = buffer; /* 4 */
  while (*ps == '.')
    ps++;
  do
    *pd++ = *ps;
  while (*ps++);
  ps = buffer; /* 5 */
  pd = shortname;
  i = 0;
  while (*ps && *ps != '.' && i++ < 8)
    *pd++ = *ps++;
  ps = ustrrchr(buffer, '.'); /* 6, 7 */
  if (ps && *++ps) {
    pd = shortname + 8;
    i = 0;
    while (*ps && i++ < 3)
      *pd++ = *ps++;
  }
}
 
/* Insere "~n" dans le dernier nom court du repertoire, de facon a ce qu'il n'y
 * ait pas deux noms courts identiques.
 *
 * lossy_conversion doit provenir de generate_basisname(). */
static void generate_numerictail(struct file_info_struct *dir_info, int lossy_conversion)
{
  unsigned int n;
 
  if (!lossy_conversion
      && ifit(dir_info->entries[dir_info->count - 1].longname, dir_info->entries[dir_info->count - 1].shortname)
      && !collide(dir_info))
    return;
  n = 1;
  do
    insert_numerictail(dir_info->entries[dir_info->count - 1].shortname, n++);
  while (collide(dir_info));
}
 
/* Genere le nom long de la derniere entree du repertoire. */
void generate_longname(struct file_info_struct *dir_info)
{
  unsigned int i;
 
  for (i = 0; dir_info->entries[dir_info->count - 1].filename[i] && i < 255; i++)
    dir_info->entries[dir_info->count - 1].longname[i] = dir_info->entries[dir_info->count - 1].filename[i];
  dir_info->entries[dir_info->count - 1].longname[i] = '\0';
}
 
/* Genere le nom court de la derniere entree du repertoire. */
void generate_shortname(struct file_info_struct *dir_info)
{
  int lossy_conversion;
 
  generate_basisname(dir_info->entries[dir_info->count - 1].longname,
                     dir_info->entries[dir_info->count - 1].shortname,
                     &lossy_conversion);
  generate_numerictail(dir_info, lossy_conversion);
}
 
/* Retourne le nombre d'entrees de repertoire supplementaires necessaires pour
 * stocker le nom long de file_info. S'il n'est pas necessaire de stocker un
 * nom long pour file_info, retourne 0. */
unsigned int n_long_entries(const struct file_info_struct *file_info)
{
  unsigned int len;
 
  for (len = 0; file_info->longname[len]; len++);
  return fit(file_info->longname, file_info->shortname) ? 0 : (len + 12) / 13;
}