/* 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 "names.h"
#include "file_info.h"
#include
#include
#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;
}