/*************************
* $Id: lfn.c,v 1.5 2001/02/08 16:01:19 harbourn Exp $
* Long Filename Processing module for Fatback
*************************
*/
#include <sys/types.h>
#include <assert.h>
#include <stdlib.h>
#include "lfn.h"
#include "dirtree.h"
#include "util.h"
static u_int8_t *unicat(u_int8_t *, int, u_int8_t *, int);
static int unistrlen(u_int8_t *, int);
static int unicopy(u_int8_t *, u_int8_t *, int);
static void lfn_assoc(dirent_t *, lfn_t *);
static char *unichoke(u_int8_t *);
static int is_legal_lfnchar(char);
/*
* Build a long file name fragment.
* returns the number of char's in the fragment
* and -1 if the buffer want a valid lfn.
*/
lfn_t *parse_lfn(u_int8_t *buf)
{
enum { SEQ_NUM_OFF = 0,
SEQ_NUM_LEN = 1,
PART_1_OFF = 1,
PART_1_LEN = 10,
ATTR_OFF = 11,
ATTR_LEN = 1,
RESVD_OFF = 12,
RESVD_LEN = 1,
CHK_SUM_OFF = 13,
CHK_SUM_LEN = 1,
PART_2_OFF = 14,
PART_2_LEN = 12,
CLUST_OFF = 26,
CLUST_LEN = 2,
PART_3_OFF = 28,
PART_3_LEN = 4,
TOTAL_LEN = PART_3_OFF + PART_3_LEN
};
int i = 0;
lfn_t *frag;
assert(buf);
/****
* Do some preliminary sanity checking
* before we load the data into a
* structure.
****/
/* Make sure that the entry isnt totaly blank! */
while (buf[i] == 0 && i < TOTAL_LEN)
i++;
if (i == TOTAL_LEN || buf[RESVD_OFF] != 0)
return NULL; /* The entry was blank or instantly invalid */
/* Check the attributes, they should be set to RHSV */
/* if (buf[ATTR_OFF] != (ATTR_RO | ATTR_HIDDEN |
* ATTR_SYSTEM | ATTR_VOLUME))
* return NULL;
*/
if ((buf[CLUST_OFF] | buf[CLUST_OFF+1]) != 0)
return NULL;
/* Now we start moving data into a structure */
frag = emalloc(sizeof *frag);
frag->next = NULL;
frag->dir = NULL;
frag->dir_seq_num = 0;
frag->lfn_seq_num = buf[SEQ_NUM_OFF];
frag->checksum = buf[CHK_SUM_OFF];
/* now concatenate all of the pieces of the name and
* store them in a single string */
{
u_int8_t *temp, *temp2, *temp3, *temp4;
int len_temp, len_temp2, len_temp3, len_temp4;
temp = emalloc(PART_1_LEN+2);
unicopy(&buf[PART_1_OFF], temp, PART_1_LEN);
temp[PART_1_LEN] = '\0';
temp[PART_1_LEN+1] = '\0';
len_temp = unistrlen(temp, PART_1_LEN);
if (len_temp < PART_1_LEN) {
frag->data = temp;
return frag;
}
temp2 = emalloc(PART_2_LEN+2);
unicopy(&buf[PART_2_OFF], temp2, PART_2_LEN);
temp2[PART_2_LEN] = '\0';
temp2[PART_2_LEN+1] = '\0';
len_temp2 = unistrlen(temp2, PART_2_LEN);
if (!len_temp2) {
frag->data = temp;
free(temp2);
return frag;
}
/*add the first two pieces together*/
temp3 = unicat(temp, len_temp, temp2, len_temp2);
len_temp3 = unistrlen(temp3, PART_1_LEN + PART_2_LEN);
free(temp);
free(temp2);
if (len_temp2 < PART_2_LEN) {
frag->data = temp3;
return frag;
}
/*grab the third piece */
temp4 = emalloc(PART_3_LEN+2);
unicopy(&buf[PART_3_OFF], temp4, PART_3_LEN);
temp4[PART_3_LEN] = '\0';
temp4[PART_3_LEN+1] = '\0';
len_temp4 = unistrlen(temp4, PART_3_LEN);
if (!len_temp4) {
frag->data = temp3;
free(temp4);
return frag;
}
/* now add on the third piece */
frag->data = unicat(temp3, len_temp3, temp4, len_temp4);
free(temp3);
free(temp4);
}
return frag;
}
/*
* Concatenate all lfn fragments in a given list
*/
void cat_lfn_list(lfn_t *lfn_list)
{
lfn_t *entry;
assert(lfn_list);
for (entry = lfn_list; entry && entry->next; entry = entry->next) {
lfn_t *next = entry->next;
while ((entry->next) &&
(entry->dir_seq_num - next->dir_seq_num == 1) && /* Reinstated */
(entry->checksum == next->checksum)) {
u_int8_t *temp = entry->data;
int temp_len = unistrlen(temp, 0);
int next_len = unistrlen(next->data, 26);
/* we can gain more accuracy by forcing the first fragment
* to be 26 bytes long, which theoreticly it should be. */
if (temp_len < 26)
break;
entry->data = unicat(temp, temp_len, next->data, next_len);
entry->next = next->next;
free(temp);
free(next->data);
free(next);
next = entry->next;
}
}
}
/*
* Recursively concatenate lfn fragments over a
* given directory tree
*/
void cat_lfn_tree(dirent_t *dir)
{
assert(dir);
if (dir->lfn_list)
cat_lfn_list(dir->lfn_list);
if (dir->child)
cat_lfn_tree(dir->child);
if (dir->next)
cat_lfn_tree(dir->next);
}
/*
* Associate all lfn's with their appropriate
* directory entry, recursively
*/
void lfn_assoc_tree(dirent_t *dir)
{
lfn_t *lfn;
assert(dir);
for (lfn = dir->lfn_list; lfn; lfn = lfn->next)
if (dir->child)
lfn_assoc(dir->child, lfn);
if (dir->child)
lfn_assoc_tree(dir->child);
if (dir->next)
lfn_assoc_tree(dir->next);
}
/*
* Convert unicode encoded long filenames in
* a directory tree into ascii
*/
void unichoke_tree(dirent_t *ent)
{
char *temp;
assert(ent);
temp = ent->lfn;
if (temp) {
ent->lfn = unichoke(ent->lfn);
free(temp);
}
if (ent->child)
unichoke_tree(ent->child);
if (ent->next)
unichoke_tree(ent->next);
}
/*
* Convert a unicode string into an ascii
* filename. (without using libc)
*/
static char *unichoke(u_int8_t *lfn)
{
char *retval;
int retval_length, lfn_length, i;
assert(lfn);
lfn_length = unistrlen(lfn, 512); /* lfn's can't be larger
* than 256 letters. */
retval_length = lfn_length / 2; /*take half the unicode size*/
if (!retval_length)
return NULL;
retval = emalloc(retval_length+1);
for (i = 0; i < (lfn_length/2); i++) {
if (!lfn[i*2] && !lfn[i*2 + 1]) {
retval[i] = '\0';
break;
}
if (is_legal_lfnchar(lfn[i*2]))
retval[i] = lfn[i*2];
else {
free(retval);
return NULL;
}
}
retval[i] = '\0';
return retval;
}
/*
* Determine if a charecter is a legal
* long filename character.
*/
static int is_legal_lfnchar(char ch)
{
static const char *legals =
" \tABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()-{}'`,.";
const char *i;
for (i = legals; i; i++)
if (*i == ch)
return 1;
return 0;
}
/*
* Associate an lfn with the apporpriate
* entry in a givin list
*/
static void lfn_assoc(dirent_t *ent_list, lfn_t *lfn)
{
dirent_t *ent;
assert(ent_list && lfn);
for (ent = ent_list; ent; ent = ent->next) {
if (ent->sequence_num == lfn->dir_seq_num + 1) {
ent->lfn = lfn->data;
lfn->data = NULL;
break;
}
}
}
/*
* Concatenate two unicode strings together
*/
static u_int8_t *unicat(u_int8_t *a, int maxa, u_int8_t *b, int maxb)
{
int new_length;
int i, length_a, length_b;
u_int8_t *new_string;
assert(a && b && maxa > 0);
length_a = unistrlen(a, maxa);
length_b = unistrlen(b, maxb);
new_length = length_a + length_b;
new_string = emalloc(new_length + 2); /* add terminator */
/* now copy a and b into new_string */
for (i = 0; i < length_a; i++)
new_string[i] = a[i];
for (; i < new_length; i++)
new_string[i] = b[i-length_a];
new_string[i++] = 0; /* add the terminator to the string */
new_string[i] = 0;
return new_string;
}
/*
* Determine the length in bytes of a unicode string
* if max is specified as zero, then it is not checked.
*/
static int unistrlen(u_int8_t *str, int max)
{
int i;
assert(str);
for (i = 0; !max || (i < max); i += 2)
if (str[i] == 0 && str[i+1] == 0)
break;
return i;
}
/*
* Copy Unicode characters from one buffer to another.
* Strings are terminated with null's which are two
* bytes wide in unicode. n is number of bytes, not unichars
* returns the number of bytes copied.
*/
static int unicopy(u_int8_t *from, u_int8_t *to, int n)
{
int i;
/* this is a lot like a regular string copy except that
* we cannot stop on a single null, we have to stop at a
* 16 bit zero value that is evenly aligned from the
* beginning of the buffer
*/
assert(from && to);
for (i = 0; i < n; i += 2) {
to[i] = from[i];
to[i+1] = from[i+1];
if (from[i] == 0 && from[i+1] == 0)
break;
}
return i;
}
syntax highlighted by Code2HTML, v. 0.9.1