2006-08-24 19:24:38 +00:00
|
|
|
/* vim: set noet ts=8 sts=8 sw=8 : */
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <wchar.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include "ecma119.h"
|
|
|
|
#include "ecma119_tree.h"
|
|
|
|
#include "tree.h"
|
|
|
|
#include "util.h"
|
2007-06-05 22:01:26 +00:00
|
|
|
#include "eltorito.h"
|
2006-08-24 19:24:38 +00:00
|
|
|
|
|
|
|
static size_t calc_dirent_len(struct ecma119_tree_node *n)
|
|
|
|
{
|
2007-05-31 04:25:39 +00:00
|
|
|
int ret = n->iso_name ? strlen(n->iso_name) + 33 : 34;
|
2006-08-24 19:24:38 +00:00
|
|
|
if (ret % 2) ret++;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Replace the file permissions and user/group id of an ECMA-119 node.
|
|
|
|
* This is used when a replace mode is selected, i.e., when we want to
|
|
|
|
* create a disc where the mode of each file or directory will be
|
|
|
|
* different than the mode in the original source.
|
|
|
|
*/
|
2007-05-31 04:25:39 +00:00
|
|
|
static void
|
|
|
|
replace_node_mode(struct ecma119_write_target *t, struct stat *st)
|
|
|
|
{
|
|
|
|
if ( S_ISDIR(st->st_mode) ) {
|
|
|
|
if ( t->replace_mode & 0x02 ) {
|
|
|
|
/* replace dir mode with specific */
|
|
|
|
st->st_mode &= S_IFMT;
|
|
|
|
st->st_mode |= t->dir_mode;
|
|
|
|
} else if (t->replace_mode & 0x01) {
|
|
|
|
/* replace dir mode with default */
|
|
|
|
/* read perm */
|
|
|
|
mode_t new_mode = (st->st_mode & S_IFMT) | 0444;
|
|
|
|
/* search bit if any */
|
|
|
|
if ( st->st_mode & 0111)
|
|
|
|
new_mode |= 0111;
|
|
|
|
st->st_mode = new_mode;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( t->replace_mode & 0x04 ) {
|
|
|
|
/* replace file mode with specific */
|
|
|
|
st->st_mode &= S_IFMT;
|
|
|
|
st->st_mode |= t->file_mode;
|
|
|
|
} else if (t->replace_mode & 0x01) {
|
|
|
|
/* replace file mode with default */
|
|
|
|
/* read perm */
|
|
|
|
mode_t new_mode = (st->st_mode & S_IFMT) | 0444;
|
|
|
|
/* execute bit if any */
|
|
|
|
if ( st->st_mode & 0111)
|
|
|
|
new_mode |= 0111;
|
|
|
|
st->st_mode = new_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if ( t->replace_mode & 0x08 ) {
|
|
|
|
/* replace gid mode with specific */
|
|
|
|
st->st_gid = t->gid;
|
|
|
|
} else if (t->replace_mode & 0x01) {
|
|
|
|
st->st_gid = 0;
|
|
|
|
}
|
|
|
|
if ( t->replace_mode & 0x10 ) {
|
|
|
|
/* replace gid mode with specific */
|
|
|
|
st->st_uid = t->uid;
|
|
|
|
} else if (t->replace_mode & 0x01) {
|
|
|
|
st->st_uid = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Creates a new ECMA-119 node from the given iso tree node, and initializes
|
|
|
|
* the fields that are common to all kind of nodes (dir, reg file, symlink...).
|
|
|
|
*
|
|
|
|
* @param t
|
|
|
|
* The options for the ECMA-119 tree that is being created
|
|
|
|
* @param parent
|
|
|
|
* The parent of the node, or NULL if it's the root.
|
|
|
|
* @param iso
|
|
|
|
* The node from which this function creates a ECMA-119 node
|
|
|
|
* @return
|
|
|
|
* The created node.
|
|
|
|
*/
|
2006-08-24 19:24:38 +00:00
|
|
|
static struct ecma119_tree_node*
|
2007-05-31 04:25:39 +00:00
|
|
|
create_ecma119_node(struct ecma119_write_target *t,
|
|
|
|
struct ecma119_tree_node *parent,
|
|
|
|
struct iso_tree_node *iso)
|
2006-08-24 19:24:38 +00:00
|
|
|
{
|
|
|
|
struct ecma119_tree_node *ret;
|
2007-05-31 04:25:39 +00:00
|
|
|
char *(*iso_name)(const char *, const char *) = ISO_ISDIR(iso) ?
|
|
|
|
((t->iso_level == 1) ? iso_1_dirid : iso_2_dirid)
|
|
|
|
: ((t->iso_level == 1) ? iso_1_fileid : iso_2_fileid);
|
2007-06-21 11:19:11 +00:00
|
|
|
char *(*iso_r_name)(const char *, const char *, int) =
|
|
|
|
ISO_ISDIR(iso) ? iso_r_dirid : iso_r_fileid;
|
2006-08-24 19:24:38 +00:00
|
|
|
|
2007-05-31 04:25:39 +00:00
|
|
|
assert(t && (!parent || parent->type == ECMA119_DIR) && iso );
|
2006-08-24 19:24:38 +00:00
|
|
|
|
|
|
|
ret = calloc(1, sizeof(struct ecma119_tree_node));
|
2007-05-31 04:25:39 +00:00
|
|
|
|
2007-06-21 11:19:11 +00:00
|
|
|
/*
|
|
|
|
* If selected one ISO relaxed constraints other than NO_DIR_REALOCATION,
|
|
|
|
* we use the function that computes the relaxed name, otherwise normal
|
|
|
|
* function for specified level is used.
|
|
|
|
*/
|
|
|
|
ret->iso_name = iso->name ?
|
|
|
|
( t->relaxed_constraints & ~ECMA119_NO_DIR_REALOCATION ?
|
|
|
|
iso_r_name(iso->name, t->input_charset, t->relaxed_constraints) :
|
|
|
|
iso_name(iso->name, t->input_charset)
|
|
|
|
) : NULL;
|
2006-08-24 19:24:38 +00:00
|
|
|
ret->dirent_len = calc_dirent_len(ret);
|
2007-07-31 07:32:26 +00:00
|
|
|
|
|
|
|
/* iso node keeps the same file attribs as the original file. */
|
2007-05-31 04:25:39 +00:00
|
|
|
ret->attrib = iso->attrib;
|
2007-07-31 07:32:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* When using RR extension and replace mode, we will replace the
|
|
|
|
* permissions and uid/gid of each file with those previously selected
|
|
|
|
* by the user.
|
|
|
|
*/
|
2007-05-31 04:25:39 +00:00
|
|
|
if ( t->rockridge && t->replace_mode )
|
|
|
|
replace_node_mode(t, &ret->attrib);
|
|
|
|
|
|
|
|
if (!iso->name)
|
|
|
|
ret->full_name = NULL;
|
|
|
|
else if ( strcmp(t->input_charset,t->ouput_charset) )
|
2007-07-31 07:32:26 +00:00
|
|
|
/* convert the file name charset */
|
2007-05-31 04:25:39 +00:00
|
|
|
ret->full_name = convert_str(iso->name, t->input_charset,
|
|
|
|
t->ouput_charset);
|
|
|
|
else
|
|
|
|
ret->full_name = strdup(iso->name);
|
2006-08-24 19:24:38 +00:00
|
|
|
ret->target = t;
|
2007-05-31 04:25:39 +00:00
|
|
|
ret->parent = parent;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Create a new ECMA-119 node representing a directory from a iso directory
|
|
|
|
* node.
|
|
|
|
*/
|
2007-05-31 04:25:39 +00:00
|
|
|
static struct ecma119_tree_node*
|
|
|
|
create_dir(struct ecma119_write_target *t,
|
|
|
|
struct ecma119_tree_node *parent,
|
|
|
|
struct iso_tree_node_dir *iso)
|
|
|
|
{
|
|
|
|
struct ecma119_tree_node *ret;
|
|
|
|
|
|
|
|
assert(t && (!parent || parent->type == ECMA119_DIR)
|
|
|
|
&& iso && S_ISDIR(iso->node.attrib.st_mode));
|
|
|
|
|
|
|
|
ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso);
|
2006-08-24 19:24:38 +00:00
|
|
|
ret->type = ECMA119_DIR;
|
2007-05-31 04:25:39 +00:00
|
|
|
ret->info.dir.real_parent = parent;
|
|
|
|
ret->info.dir.depth = parent ? parent->info.dir.depth + 1 : 1;
|
|
|
|
ret->info.dir.nchildren = 0;
|
|
|
|
ret->info.dir.children = calloc(1, sizeof(void*) * iso->nchildren);
|
2006-08-24 19:24:38 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Create a new ECMA-119 node representing a regular file from a iso file
|
|
|
|
* node.
|
|
|
|
*/
|
2006-08-24 19:24:38 +00:00
|
|
|
static struct ecma119_tree_node*
|
|
|
|
create_file(struct ecma119_write_target *t,
|
|
|
|
struct ecma119_tree_node *parent,
|
2007-05-31 04:25:39 +00:00
|
|
|
struct iso_tree_node_file *iso)
|
2006-08-24 19:24:38 +00:00
|
|
|
{
|
|
|
|
struct ecma119_tree_node *ret;
|
2007-05-31 04:25:39 +00:00
|
|
|
struct iso_file *file;
|
2006-08-24 19:24:38 +00:00
|
|
|
|
|
|
|
assert(t && iso && parent && parent->type == ECMA119_DIR);
|
|
|
|
|
2007-05-31 04:25:39 +00:00
|
|
|
ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso);
|
|
|
|
ret->type = ECMA119_FILE;
|
|
|
|
|
|
|
|
/* get iso_file struct */
|
|
|
|
file = iso_file_table_lookup(t->file_table, iso);
|
|
|
|
if ( file == NULL ) {
|
2007-07-31 07:32:26 +00:00
|
|
|
/*
|
|
|
|
* If the file is not already added to the disc, we add it now
|
|
|
|
* to the file table, and get a new inode number for it.
|
|
|
|
*/
|
2007-09-26 07:47:45 +00:00
|
|
|
file = iso_file_new(t, iso);
|
|
|
|
if (!file) {
|
|
|
|
/*
|
|
|
|
* That was an error.
|
|
|
|
* TODO currently this cause the file to be ignored... Maybe
|
|
|
|
* throw an error is a better alternative
|
|
|
|
*/
|
|
|
|
ecma119_tree_free(ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-05-31 04:25:39 +00:00
|
|
|
iso_file_table_add_file(t->file_table, file);
|
|
|
|
file->ino = ++t->ino;
|
|
|
|
} else {
|
|
|
|
/* increment number of hard-links */
|
|
|
|
file->nlink++;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret->attrib.st_nlink = file->nlink;
|
|
|
|
ret->attrib.st_ino = file->ino;
|
|
|
|
ret->info.file = file;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Create a new ECMA-119 node representing a placeholder for a relocated
|
|
|
|
* dir.
|
|
|
|
*
|
|
|
|
* See IEEE P1282, section 4.1.5 for details
|
|
|
|
*/
|
2007-05-31 04:25:39 +00:00
|
|
|
static struct ecma119_tree_node*
|
|
|
|
create_placeholder(struct ecma119_write_target *t,
|
|
|
|
struct ecma119_tree_node *parent,
|
|
|
|
struct ecma119_tree_node *real)
|
|
|
|
{
|
|
|
|
struct ecma119_tree_node *ret;
|
|
|
|
|
|
|
|
assert(t && real && real->type == ECMA119_DIR
|
|
|
|
&& parent && parent->type == ECMA119_DIR);
|
|
|
|
|
2006-08-24 19:24:38 +00:00
|
|
|
ret = calloc(1, sizeof(struct ecma119_tree_node));
|
2007-05-31 04:25:39 +00:00
|
|
|
ret->iso_name = real->iso_name; /* TODO strdup? */
|
|
|
|
/* FIXME
|
|
|
|
* if we strdup above, if name changes in mangle_all,
|
|
|
|
* this probably keeps as original.
|
|
|
|
* if not, both change, but we need to update dirent_len.
|
|
|
|
* I think that attributes of a placeholder must be taken from
|
|
|
|
* real_me, not keept here.
|
|
|
|
* FIXME
|
|
|
|
* Another question is that real is a dir, while placeholder is
|
|
|
|
* a file, and ISO name restricctions are different, what to do?
|
|
|
|
*/
|
|
|
|
ret->dirent_len = real->dirent_len;
|
|
|
|
ret->attrib = real->attrib;
|
|
|
|
ret->full_name = strdup(real->full_name);
|
2006-08-24 19:24:38 +00:00
|
|
|
ret->target = t;
|
2007-05-31 04:25:39 +00:00
|
|
|
ret->parent = parent;
|
|
|
|
ret->type = ECMA119_PLACEHOLDER;
|
|
|
|
ret->info.real_me = real;
|
|
|
|
ret->attrib.st_nlink = 1;
|
|
|
|
ret->attrib.st_ino = ++t->ino;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Create a new ECMA-119 node representing a symbolic link from a iso symlink
|
|
|
|
* node.
|
|
|
|
*/
|
2007-05-31 04:25:39 +00:00
|
|
|
static struct ecma119_tree_node*
|
|
|
|
create_symlink(struct ecma119_write_target *t,
|
|
|
|
struct ecma119_tree_node *parent,
|
|
|
|
struct iso_tree_node_symlink *iso)
|
|
|
|
{
|
|
|
|
struct ecma119_tree_node *ret;
|
|
|
|
|
|
|
|
assert(t && iso && parent && parent->type == ECMA119_DIR);
|
|
|
|
|
|
|
|
ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso);
|
|
|
|
ret->type = ECMA119_SYMLINK;
|
|
|
|
ret->info.dest = iso->dest; /* TODO strdup? */
|
|
|
|
ret->attrib.st_nlink = 1;
|
|
|
|
ret->attrib.st_ino = ++t->ino;
|
2006-08-24 19:24:38 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
2007-09-01 20:35:53 +00:00
|
|
|
* Create a new ECMA-119 node representing a boot catalog or image.
|
|
|
|
* This will be treated as a normal file when written the directory record,
|
|
|
|
* but its contents are written in a different way.
|
2007-07-31 07:32:26 +00:00
|
|
|
*
|
|
|
|
* See "El Torito" Bootable CD-ROM Format Specification Version 1.0 for
|
|
|
|
* more details.
|
|
|
|
*/
|
2007-06-05 22:01:26 +00:00
|
|
|
static struct ecma119_tree_node*
|
2007-09-01 20:35:53 +00:00
|
|
|
create_boot(struct ecma119_write_target *t,
|
2007-06-05 22:01:26 +00:00
|
|
|
struct ecma119_tree_node *parent,
|
2007-09-01 20:35:53 +00:00
|
|
|
struct iso_tree_node_boot *iso)
|
2007-06-05 22:01:26 +00:00
|
|
|
{
|
|
|
|
struct ecma119_tree_node *ret;
|
|
|
|
|
|
|
|
assert(t && iso && parent && parent->type == ECMA119_DIR);
|
|
|
|
|
|
|
|
ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso);
|
2007-09-01 20:35:53 +00:00
|
|
|
ret->type = ECMA119_BOOT;
|
2007-06-05 22:01:26 +00:00
|
|
|
|
2007-09-01 20:35:53 +00:00
|
|
|
ret->info.boot_img = iso->img;
|
2007-06-05 22:01:26 +00:00
|
|
|
|
2007-09-01 20:35:53 +00:00
|
|
|
ret->attrib.st_nlink = 1;
|
|
|
|
ret->attrib.st_ino = ++t->ino;
|
2007-06-05 22:01:26 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Create a new ECMA-119 node that corresponds to the given iso tree node.
|
|
|
|
* If that node is a dir, this function recurses over all their children,
|
|
|
|
* thus creating a ECMA-119 tree whose root is the given iso dir.
|
|
|
|
*/
|
2006-08-24 19:24:38 +00:00
|
|
|
static struct ecma119_tree_node*
|
|
|
|
create_tree(struct ecma119_write_target *t,
|
|
|
|
struct ecma119_tree_node *parent,
|
|
|
|
struct iso_tree_node *iso)
|
|
|
|
{
|
2007-05-31 04:25:39 +00:00
|
|
|
struct ecma119_tree_node *ret = NULL;
|
2006-08-24 19:24:38 +00:00
|
|
|
|
|
|
|
assert(t && iso);
|
2007-05-31 04:25:39 +00:00
|
|
|
|
|
|
|
if ( iso->hide_flags & LIBISO_HIDE_ON_RR )
|
|
|
|
return NULL;
|
|
|
|
|
2007-06-05 22:01:26 +00:00
|
|
|
switch ( iso->type ) {
|
|
|
|
case LIBISO_NODE_FILE:
|
2007-05-31 04:25:39 +00:00
|
|
|
ret = create_file(t, parent, (struct iso_tree_node_file*)iso);
|
|
|
|
break;
|
2007-06-05 22:01:26 +00:00
|
|
|
case LIBISO_NODE_SYMLINK:
|
2007-05-31 04:25:39 +00:00
|
|
|
if ( !t->rockridge )
|
|
|
|
printf("Can't add symlinks to a non ISO tree. Skipping %s \n",
|
|
|
|
iso->name);
|
|
|
|
else
|
|
|
|
ret = create_symlink(t, parent, (struct iso_tree_node_symlink*)iso);
|
|
|
|
break;
|
2007-06-05 22:01:26 +00:00
|
|
|
case LIBISO_NODE_DIR:
|
2007-05-31 04:25:39 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
struct iso_tree_node_dir *dir = (struct iso_tree_node_dir*)iso;
|
|
|
|
ret = create_dir(t, parent, dir);
|
|
|
|
for (i = 0; i < dir->nchildren; i++) {
|
|
|
|
struct ecma119_tree_node *child;
|
|
|
|
child = create_tree(t, ret, dir->children[i]);
|
|
|
|
if (child)
|
|
|
|
ret->info.dir.children[ret->info.dir.nchildren++] = child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2007-09-01 20:35:53 +00:00
|
|
|
case LIBISO_NODE_BOOT:
|
|
|
|
ret = create_boot(t, parent, (struct iso_tree_node_boot*)iso);
|
2007-06-05 22:01:26 +00:00
|
|
|
break;
|
2007-05-31 04:25:39 +00:00
|
|
|
default:
|
|
|
|
/* should never happen */
|
|
|
|
assert( 0 );
|
|
|
|
break;
|
2006-08-24 19:24:38 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ecma119_tree_free(struct ecma119_tree_node *root)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (root->type == ECMA119_DIR) {
|
2007-05-31 04:25:39 +00:00
|
|
|
for (i=0; i < root->info.dir.nchildren; i++) {
|
|
|
|
ecma119_tree_free(root->info.dir.children[i]);
|
2006-08-24 19:24:38 +00:00
|
|
|
}
|
2007-05-31 04:25:39 +00:00
|
|
|
free(root->info.dir.children);
|
2006-08-24 19:24:38 +00:00
|
|
|
}
|
2007-05-31 04:25:39 +00:00
|
|
|
free(root->iso_name);
|
|
|
|
free(root->full_name);
|
2006-08-24 19:24:38 +00:00
|
|
|
free(root);
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
max_child_name_len(struct ecma119_tree_node *root)
|
|
|
|
{
|
|
|
|
size_t ret = 0, i;
|
|
|
|
|
|
|
|
assert(root->type == ECMA119_DIR);
|
2007-05-31 04:25:39 +00:00
|
|
|
for (i=0; i < root->info.dir.nchildren; i++) {
|
|
|
|
size_t len = strlen(root->info.dir.children[i]->iso_name);
|
2006-08-24 19:24:38 +00:00
|
|
|
ret = MAX(ret, len);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Relocates a directory, as specified in Rock Ridge Specification
|
|
|
|
* (see IEEE P1282, section 4.1.5). This is needed when the number of levels
|
|
|
|
* on a directory hierarchy exceeds 8, or the length of a path is higher
|
|
|
|
* than 255 characters, as specified in ECMA-119, section 6.8.2.1
|
|
|
|
*/
|
2006-08-24 19:24:38 +00:00
|
|
|
static void
|
|
|
|
reparent(struct ecma119_tree_node *child,
|
|
|
|
struct ecma119_tree_node *parent)
|
|
|
|
{
|
|
|
|
int found = 0;
|
|
|
|
size_t i;
|
|
|
|
struct ecma119_tree_node *placeholder;
|
|
|
|
|
|
|
|
assert(child && parent && parent->type == ECMA119_DIR && child->parent);
|
|
|
|
|
|
|
|
/* replace the child in the original parent with a placeholder */
|
2007-05-31 04:25:39 +00:00
|
|
|
for (i=0; i < child->parent->info.dir.nchildren; i++) {
|
|
|
|
if (child->parent->info.dir.children[i] == child) {
|
|
|
|
placeholder = create_placeholder(child->target,
|
|
|
|
child->parent, child);
|
|
|
|
child->parent->info.dir.children[i] = placeholder;
|
2006-08-24 19:24:38 +00:00
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(found);
|
|
|
|
|
|
|
|
/* add the child to its new parent */
|
|
|
|
child->parent = parent;
|
2007-05-31 04:25:39 +00:00
|
|
|
parent->info.dir.nchildren++;
|
|
|
|
parent->info.dir.children = realloc( parent->info.dir.children,
|
|
|
|
sizeof(void*) * parent->info.dir.nchildren );
|
|
|
|
parent->info.dir.children[parent->info.dir.nchildren-1] = child;
|
2006-08-24 19:24:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reorder the tree, if necessary, to ensure that
|
|
|
|
* - the depth is at most 8
|
|
|
|
* - each path length is at most 255 characters
|
2007-07-31 07:32:26 +00:00
|
|
|
* This restriction is imposed by ECMA-119 specification (see ECMA-119,
|
|
|
|
* 6.8.2.1).
|
2006-08-24 19:24:38 +00:00
|
|
|
*/
|
|
|
|
static void
|
2007-05-31 04:25:39 +00:00
|
|
|
reorder_tree(struct ecma119_write_target *t,
|
|
|
|
struct ecma119_tree_node *root,
|
2006-08-24 19:24:38 +00:00
|
|
|
struct ecma119_tree_node *cur)
|
|
|
|
{
|
|
|
|
size_t max_path;
|
|
|
|
|
|
|
|
assert(root && cur && cur->type == ECMA119_DIR);
|
|
|
|
|
2007-05-31 04:25:39 +00:00
|
|
|
cur->info.dir.depth = cur->parent ? cur->parent->info.dir.depth + 1 : 1;
|
|
|
|
cur->info.dir.path_len = cur->parent ? cur->parent->info.dir.path_len
|
|
|
|
+ strlen(cur->iso_name) : 0;
|
|
|
|
max_path = cur->info.dir.path_len + cur->info.dir.depth
|
|
|
|
+ max_child_name_len(cur);
|
|
|
|
|
|
|
|
if (cur->info.dir.depth > 8 || max_path > 255) {
|
|
|
|
|
|
|
|
if (t->rockridge) {
|
|
|
|
reparent(cur, root);
|
|
|
|
/* we are appended to the root's children now, so there is no
|
|
|
|
* need to recurse (the root will hit us again) */
|
|
|
|
} else {
|
|
|
|
/* we need to delete cur */
|
|
|
|
size_t i,j;
|
|
|
|
struct ecma119_tree_node *parent = cur->parent;
|
|
|
|
|
|
|
|
printf("Can't dirs deeper than 8 without RR. Skipping %s\n",
|
|
|
|
cur->full_name);
|
|
|
|
for (i=0; i < parent->info.dir.nchildren; ++i) {
|
|
|
|
if (parent->info.dir.children[i] == cur) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert ( i < parent->info.dir.nchildren);
|
|
|
|
for ( j = i; j < parent->info.dir.nchildren - 1; ++j)
|
|
|
|
parent->info.dir.children[j] = parent->info.dir.children[j+1];
|
|
|
|
parent->info.dir.nchildren--;
|
|
|
|
ecma119_tree_free(cur);
|
|
|
|
}
|
2006-08-24 19:24:38 +00:00
|
|
|
} else {
|
|
|
|
size_t i;
|
|
|
|
|
2007-05-31 04:25:39 +00:00
|
|
|
for (i=0; i < cur->info.dir.nchildren; i++) {
|
|
|
|
if (cur->info.dir.children[i]->type == ECMA119_DIR)
|
|
|
|
reorder_tree(t, root, cur->info.dir.children[i]);
|
2006-08-24 19:24:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Compare the iso name of two ECMA-119 nodes
|
|
|
|
*/
|
2006-08-24 19:24:38 +00:00
|
|
|
static int
|
|
|
|
cmp_node(const void *f1, const void *f2)
|
|
|
|
{
|
|
|
|
struct ecma119_tree_node *f = *((struct ecma119_tree_node**)f1);
|
|
|
|
struct ecma119_tree_node *g = *((struct ecma119_tree_node**)f2);
|
2007-05-31 04:25:39 +00:00
|
|
|
return strcmp(f->iso_name, g->iso_name);
|
2006-08-24 19:24:38 +00:00
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Sorts a the children of each directory in the ECMA-119 tree represented
|
|
|
|
* by \p root, acording to the order specified in ECMA-119, section 9.3.
|
|
|
|
*/
|
2006-08-24 19:24:38 +00:00
|
|
|
static void
|
|
|
|
sort_tree(struct ecma119_tree_node *root)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
assert(root && root->type == ECMA119_DIR);
|
|
|
|
|
2007-05-31 04:25:39 +00:00
|
|
|
qsort(root->info.dir.children, root->info.dir.nchildren,
|
|
|
|
sizeof(void*), cmp_node);
|
|
|
|
for (i=0; i < root->info.dir.nchildren; i++) {
|
|
|
|
if (root->info.dir.children[i]->type == ECMA119_DIR)
|
|
|
|
sort_tree(root->info.dir.children[i]);
|
2006-08-24 19:24:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Change num_change characters of the given filename in order to ensure the
|
|
|
|
* name is unique. If the name is short enough (depending on the ISO level),
|
|
|
|
* we can append the characters instead of changing them.
|
|
|
|
*
|
|
|
|
* \p seq_num is the index of this file in the sequence of identical filenames.
|
|
|
|
*
|
|
|
|
* For example, seq_num=3, num_change=2, name="HELLOTHERE.TXT" changes name to
|
|
|
|
* "HELLOTHE03.TXT"
|
|
|
|
*/
|
|
|
|
static void
|
2007-08-10 09:30:26 +00:00
|
|
|
mangle_name(char **name, int num_change, int level, int relaxed, int seq_num)
|
2006-08-24 19:24:38 +00:00
|
|
|
{
|
|
|
|
char *dot = strrchr(*name, '.');
|
|
|
|
char *semi = strrchr(*name, ';');
|
|
|
|
size_t len = strlen(*name);
|
|
|
|
char base[len+1], ext[len+1];
|
|
|
|
char fmt[12];
|
|
|
|
size_t baselen, extlen;
|
|
|
|
|
|
|
|
if (num_change >= len) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
strncpy(base, *name, len+1);
|
2007-08-10 09:30:26 +00:00
|
|
|
|
|
|
|
if (relaxed & ECMA119_RELAXED_FILENAMES) {
|
|
|
|
/* relaxed filenames, don't care about extension */
|
|
|
|
int maxlen = (relaxed & (1<<1)) ? 37 : 31;
|
|
|
|
base[maxlen - num_change] = '\0';
|
|
|
|
baselen = strlen(base);
|
|
|
|
if (relaxed & ECMA119_OMIT_VERSION_NUMBERS) {
|
|
|
|
sprintf(fmt, "%%s%%0%1dd", num_change);
|
|
|
|
*name = realloc(*name, baselen + num_change + 1);
|
|
|
|
} else {
|
|
|
|
sprintf(fmt, "%%s%%0%1dd;1", num_change);
|
|
|
|
*name = realloc(*name, baselen + num_change + 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(*name, fmt, base, seq_num);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-08-24 19:24:38 +00:00
|
|
|
if (dot) {
|
|
|
|
base[dot - *name] = '\0';
|
|
|
|
strncpy(ext, dot+1, len+1);
|
|
|
|
if (semi) {
|
|
|
|
ext[semi - dot - 1] = '\0';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
base[len-2] = '\0';
|
|
|
|
ext[0] = '\0';
|
|
|
|
}
|
|
|
|
baselen = strlen(base);
|
|
|
|
extlen = strlen(ext);
|
2007-08-10 09:30:26 +00:00
|
|
|
|
|
|
|
if (relaxed & (1<<1)) {
|
|
|
|
/* 37 char filenames */
|
|
|
|
base[36 - extlen - num_change] = '\0';
|
|
|
|
} else if (level == 1 && baselen + num_change > 8) {
|
2006-08-24 19:24:38 +00:00
|
|
|
base[8 - num_change] = '\0';
|
|
|
|
} else if (level != 1 && baselen + extlen + num_change > 30) {
|
|
|
|
base[30 - extlen - num_change] = '\0';
|
|
|
|
}
|
|
|
|
|
2007-08-10 09:30:26 +00:00
|
|
|
if (relaxed & ECMA119_OMIT_VERSION_NUMBERS) {
|
|
|
|
sprintf(fmt, "%%s%%0%1dd.%%s", num_change);
|
|
|
|
*name = realloc(*name, baselen + extlen + num_change + 1);
|
|
|
|
} else {
|
|
|
|
sprintf(fmt, "%%s%%0%1dd.%%s;1", num_change);
|
|
|
|
*name = realloc(*name, baselen + extlen + num_change + 4);
|
|
|
|
}
|
|
|
|
|
2006-08-24 19:24:38 +00:00
|
|
|
sprintf(*name, fmt, base, seq_num, ext);
|
|
|
|
}
|
|
|
|
|
2007-07-31 07:32:26 +00:00
|
|
|
/**
|
|
|
|
* Ensures that the ISO name of each children of the given dir is unique,
|
|
|
|
* changing some of them if needed.
|
|
|
|
*/
|
2006-08-24 19:24:38 +00:00
|
|
|
static void
|
|
|
|
mangle_all(struct ecma119_tree_node *dir)
|
|
|
|
{
|
|
|
|
size_t i, j, k;
|
2007-05-31 04:25:39 +00:00
|
|
|
struct ecma119_dir_info d = dir->info.dir;
|
2006-08-24 19:24:38 +00:00
|
|
|
size_t n_change;
|
|
|
|
int changed;
|
|
|
|
|
|
|
|
assert(dir->type == ECMA119_DIR);
|
|
|
|
do {
|
|
|
|
changed = 0;
|
|
|
|
for (i=0; i < d.nchildren; i++) {
|
|
|
|
/* find the number of consecutive equal names */
|
|
|
|
j = 1;
|
|
|
|
while ( i+j < d.nchildren &&
|
2007-05-31 04:25:39 +00:00
|
|
|
!strcmp(d.children[i]->iso_name,
|
|
|
|
d.children[i+j]->iso_name) )
|
2006-08-24 19:24:38 +00:00
|
|
|
j++;
|
|
|
|
if (j == 1) continue;
|
|
|
|
|
|
|
|
/* mangle the names */
|
|
|
|
changed = 1;
|
|
|
|
n_change = j / 10 + 1;
|
|
|
|
for (k=0; k < j; k++) {
|
2007-05-31 04:25:39 +00:00
|
|
|
mangle_name(&(d.children[i+k]->iso_name),
|
2006-08-24 19:24:38 +00:00
|
|
|
n_change,
|
|
|
|
dir->target->iso_level,
|
2007-08-10 09:30:26 +00:00
|
|
|
dir->target->relaxed_constraints,
|
2006-08-24 19:24:38 +00:00
|
|
|
k);
|
|
|
|
d.children[i+k]->dirent_len =
|
|
|
|
calc_dirent_len(d.children[i+k]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* skip ahead by the number of mangled names */
|
|
|
|
i += j - 1;
|
|
|
|
}
|
|
|
|
} while (changed);
|
|
|
|
|
|
|
|
for (i=0; i < d.nchildren; i++) {
|
|
|
|
if (d.children[i]->type == ECMA119_DIR)
|
|
|
|
mangle_all(d.children[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ecma119_tree_node*
|
|
|
|
ecma119_tree_create(struct ecma119_write_target *t,
|
|
|
|
struct iso_tree_node *iso_root)
|
|
|
|
{
|
|
|
|
t->root = create_tree(t, NULL, iso_root);
|
2007-06-21 11:19:11 +00:00
|
|
|
if ( !(t->relaxed_constraints & ECMA119_NO_DIR_REALOCATION) )
|
|
|
|
reorder_tree(t, t->root, t->root);
|
2006-08-24 19:24:38 +00:00
|
|
|
sort_tree(t->root);
|
|
|
|
mangle_all(t->root);
|
|
|
|
return t->root;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ecma119_tree_print(struct ecma119_tree_node *root, int spaces)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
char sp[spaces+1];
|
|
|
|
|
|
|
|
memset(sp, ' ', spaces);
|
|
|
|
sp[spaces] = '\0';
|
|
|
|
|
2007-05-31 04:25:39 +00:00
|
|
|
printf("%s%s\n", sp, root->iso_name);
|
2006-08-24 19:24:38 +00:00
|
|
|
if (root->type == ECMA119_DIR)
|
2007-05-31 04:25:39 +00:00
|
|
|
for (i=0; i < root->info.dir.nchildren; i++)
|
|
|
|
ecma119_tree_print(root->info.dir.children[i], spaces+2);
|
2006-08-24 19:24:38 +00:00
|
|
|
}
|