diff options
Diffstat (limited to 'src/input/vcd/libvcd/directory.c')
-rw-r--r-- | src/input/vcd/libvcd/directory.c | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/src/input/vcd/libvcd/directory.c b/src/input/vcd/libvcd/directory.c new file mode 100644 index 000000000..9df691f99 --- /dev/null +++ b/src/input/vcd/libvcd/directory.c @@ -0,0 +1,506 @@ +/* + $Id: directory.c,v 1.1 2003/10/13 11:47:12 f1rmb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +/* Public headers */ +#include <cdio/iso9660.h> +#include <libvcd/logging.h> + +/* Private headers */ +#include "assert.h" +#include "bytesex.h" +#include "directory.h" +#include "util.h" + +static const char _rcsid[] = "$Id: directory.c,v 1.1 2003/10/13 11:47:12 f1rmb Exp $"; + +/* CD-ROM XA */ + +/* tree data structure */ + +typedef struct +{ + bool is_dir; + char *name; + uint16_t version; + uint16_t xa_attributes; + uint8_t xa_filenum; + uint32_t extent; + uint32_t size; + unsigned pt_id; +} data_t; + +typedef VcdTreeNode VcdDirNode; + +#define EXTENT(anode) (DATAP(anode)->extent) +#define SIZE(anode) (DATAP(anode)->size) +#define PT_ID(anode) (DATAP(anode)->pt_id) + +#define DATAP(anode) ((data_t*) _vcd_tree_node_data (anode)) + +/* important date to celebrate (for me at least =) + -- until user customization is implemented... */ +static const time_t _vcd_time = 269222400L; + +/* implementation */ + +static void +traverse_get_dirsizes(VcdDirNode *node, void *data) +{ + data_t *d = DATAP(node); + unsigned *sum = data; + + if (d->is_dir) + { + vcd_assert (d->size % ISO_BLOCKSIZE == 0); + + *sum += (d->size / ISO_BLOCKSIZE); + } +} + +static unsigned +get_dirsizes (VcdDirNode* dirnode) +{ + unsigned result = 0; + + _vcd_tree_node_traverse (dirnode, traverse_get_dirsizes, &result); + + return result; +} + +static void +traverse_update_dirextents (VcdDirNode *dirnode, void *data) +{ + data_t *d = DATAP(dirnode); + + if (d->is_dir) + { + VcdDirNode* child; + unsigned dirextent = d->extent; + + vcd_assert (d->size % ISO_BLOCKSIZE == 0); + + dirextent += d->size / ISO_BLOCKSIZE; + + _VCD_CHILD_FOREACH (child, dirnode) + { + data_t *d = DATAP(child); + + vcd_assert (d != NULL); + + if (d->is_dir) + { + d->extent = dirextent; + dirextent += get_dirsizes (child); + } + } + } +} + +static void +update_dirextents (VcdDirectory *dir, uint32_t extent) +{ + data_t *dirdata = DATAP(_vcd_tree_root (dir)); + + dirdata->extent = extent; + _vcd_tree_node_traverse (_vcd_tree_root (dir), + traverse_update_dirextents, NULL); +} + +static void +traverse_update_sizes(VcdDirNode *node, void *data) +{ + data_t *dirdata = DATAP(node); + + if (dirdata->is_dir) + { + VcdDirNode* child; + unsigned offset = 0; + + offset += iso9660_dir_calc_record_size (1, sizeof(iso9660_xa_t)); /* '.' */ + offset += iso9660_dir_calc_record_size (1, sizeof(iso9660_xa_t)); /* '..' */ + + _VCD_CHILD_FOREACH (child, node) + { + data_t *d = DATAP(child); + unsigned reclen; + char *pathname = (d->is_dir + ? strdup (d->name) + : iso9660_pathname_isofy (d->name, d->version)); + + vcd_assert (d != NULL); + + reclen = iso9660_dir_calc_record_size (strlen (pathname), + sizeof (iso9660_xa_t)); + + free (pathname); + + offset = _vcd_ofs_add (offset, reclen, ISO_BLOCKSIZE); + } + + vcd_assert (offset > 0); + + dirdata->size = _vcd_ceil2block (offset, ISO_BLOCKSIZE); + } +} + +static void +update_sizes (VcdDirectory *dir) +{ + _vcd_tree_node_traverse (_vcd_tree_root(dir), traverse_update_sizes, NULL); +} + + +/* exported stuff: */ + +VcdDirectory * +_vcd_directory_new (void) +{ + data_t *data; + VcdDirectory *dir = NULL; + + vcd_assert (sizeof(iso9660_xa_t) == 14); + + data = _vcd_malloc (sizeof (data_t)); + dir = _vcd_tree_new (data); + + data->is_dir = true; + data->name = _vcd_memdup("\0", 2); + data->xa_attributes = XA_FORM1_DIR; + data->xa_filenum = 0x00; + + return dir; +} + +static void +traverse_vcd_directory_done (VcdDirNode *node, void *data) +{ + data_t *dirdata = DATAP (node); + + free (dirdata->name); +} + +void +_vcd_directory_destroy (VcdDirectory *dir) +{ + vcd_assert (dir != NULL); + + _vcd_tree_node_traverse (_vcd_tree_root (dir), + traverse_vcd_directory_done, NULL); + + _vcd_tree_destroy (dir, true); +} + +static VcdDirNode* +lookup_child (VcdDirNode* node, const char name[]) +{ + VcdDirNode* child; + + _VCD_CHILD_FOREACH (child, node) + { + data_t *d = DATAP(child); + + if (!strcmp (d->name, name)) + return child; + } + + return child; /* NULL */ +} + +static int +_iso_dir_cmp (VcdDirNode *node1, VcdDirNode *node2) +{ + data_t *d1 = DATAP(node1); + data_t *d2 = DATAP(node2); + int result = 0; + + result = strcmp (d1->name, d2->name); + + return result; +} + +int +_vcd_directory_mkdir (VcdDirectory *dir, const char pathname[]) +{ + char **splitpath; + unsigned level, n; + VcdDirNode* pdir = _vcd_tree_root (dir); + + vcd_assert (dir != NULL); + vcd_assert (pathname != NULL); + + splitpath = _vcd_strsplit (pathname, '/'); + + level = _vcd_strlenv (splitpath); + + for (n = 0; n < level-1; n++) + if (!(pdir = lookup_child(pdir, splitpath[n]))) + { + vcd_error("mkdir: parent dir `%s' (level=%d) for `%s' missing!", + splitpath[n], n, pathname); + vcd_assert_not_reached (); + } + + if (lookup_child (pdir, splitpath[level-1])) + { + vcd_error ("mkdir: `%s' already exists", pathname); + vcd_assert_not_reached (); + } + + { + data_t *data = _vcd_malloc (sizeof (data_t)); + + _vcd_tree_node_append_child (pdir, data); + + data->is_dir = true; + data->name = strdup(splitpath[level-1]); + data->xa_attributes = XA_FORM1_DIR; + data->xa_filenum = 0x00; + /* .. */ + } + + _vcd_tree_node_sort_children (pdir, _iso_dir_cmp); + + _vcd_strfreev (splitpath); + + return 0; +} + +int +_vcd_directory_mkfile (VcdDirectory *dir, const char pathname[], + uint32_t start, uint32_t size, + bool form2_flag, uint8_t filenum) +{ + char **splitpath; + unsigned level, n; + const int file_version = 1; + + VcdDirNode* pdir = NULL; + + vcd_assert (dir != NULL); + vcd_assert (pathname != NULL); + + splitpath = _vcd_strsplit (pathname, '/'); + + level = _vcd_strlenv (splitpath); + + while (!pdir) + { + pdir = _vcd_tree_root (dir); + + for (n = 0; n < level-1; n++) + if (!(pdir = lookup_child (pdir, splitpath[n]))) + { + char *newdir = _vcd_strjoin (splitpath, n+1, "/"); + + vcd_info ("autocreating directory `%s' for file `%s'", + newdir, pathname); + + _vcd_directory_mkdir (dir, newdir); + + free (newdir); + + vcd_assert (pdir == NULL); + + break; + } + else if (!DATAP(pdir)->is_dir) + { + char *newdir = _vcd_strjoin (splitpath, n+1, "/"); + + vcd_error ("mkfile: `%s' not a directory", newdir); + + free (newdir); + + return -1; + } + + } + + if (lookup_child (pdir, splitpath[level-1])) + { + vcd_error ("mkfile: `%s' already exists", pathname); + return -1; + } + + { + data_t *data = _vcd_malloc (sizeof (data_t)); + + _vcd_tree_node_append_child (pdir, data); + + data->is_dir = false; + data->name = strdup (splitpath[level-1]); + data->version = file_version; + data->xa_attributes = form2_flag ? XA_FORM2_FILE : XA_FORM1_FILE; + data->xa_filenum = filenum; + data->size = size; + data->extent = start; + /* .. */ + } + + _vcd_tree_node_sort_children (pdir, _iso_dir_cmp); + + _vcd_strfreev (splitpath); + + return 0; +} + +uint32_t +_vcd_directory_get_size (VcdDirectory *dir) +{ + vcd_assert (dir != NULL); + + update_sizes (dir); + return get_dirsizes (_vcd_tree_root (dir)); +} + +static void +traverse_vcd_directory_dump_entries (VcdDirNode *node, void *data) +{ + data_t *d = DATAP(node); + iso9660_xa_t xa_su; + + uint32_t root_extent = EXTENT(_vcd_tree_node_root (node)); + + uint32_t parent_extent = + (!_vcd_tree_node_is_root (node)) + ? EXTENT(_vcd_tree_node_parent (node)) + : EXTENT(node); + + uint32_t parent_size = + (!_vcd_tree_node_is_root (node)) + ? SIZE(_vcd_tree_node_parent (node)) + : SIZE(node); + + void *dirbufp = (char*) data + ISO_BLOCKSIZE * (parent_extent - root_extent); + + iso9660_xa_init (&xa_su, 0, 0, d->xa_attributes, d->xa_filenum); + + if (!_vcd_tree_node_is_root (node)) + { + char *pathname = (d->is_dir + ? strdup (d->name) + : iso9660_pathname_isofy (d->name, d->version)); + + iso9660_dir_add_entry_su (dirbufp, pathname, d->extent, d->size, + d->is_dir ? ISO_DIRECTORY : ISO_FILE, + &xa_su, sizeof (xa_su), + &_vcd_time); + + free (pathname); + } + + /* if this is a directory, create the new directory node */ + if (d->is_dir) + { + dirbufp = (char*)data + ISO_BLOCKSIZE * (d->extent - root_extent); + + iso9660_dir_init_new_su (dirbufp, + d->extent, d->size, &xa_su, sizeof (xa_su), + parent_extent, parent_size, &xa_su, + sizeof (xa_su), &_vcd_time); + } +} + +void +_vcd_directory_dump_entries (VcdDirectory *dir, void *buf, uint32_t extent) +{ + vcd_assert (dir != NULL); + + update_sizes (dir); /* better call it one time more than one less */ + update_dirextents (dir, extent); + + _vcd_tree_node_traverse (_vcd_tree_root (dir), + traverse_vcd_directory_dump_entries, buf); +} + +typedef struct +{ + void *ptl; + void *ptm; +} _vcd_directory_dump_pathtables_t; + +static void +_dump_pathtables_helper (_vcd_directory_dump_pathtables_t *args, + data_t *d, uint16_t parent_id) +{ + uint16_t id_l, id_m; + + vcd_assert (args != NULL); + vcd_assert (d != NULL); + + vcd_assert (d->is_dir); + + id_l = iso9660_pathtable_l_add_entry (args->ptl, d->name, d->extent, + parent_id); + + id_m = iso9660_pathtable_m_add_entry (args->ptm, d->name, d->extent, + parent_id); + + vcd_assert (id_l == id_m); + + d->pt_id = id_m; +} + +static void +traverse_vcd_directory_dump_pathtables (VcdDirNode *node, void *data) +{ + _vcd_directory_dump_pathtables_t *args = data; + + if (DATAP (node)->is_dir) + { + VcdDirNode* parent = _vcd_tree_node_parent (node); + uint16_t parent_id = parent ? PT_ID (parent) : 1; + + _dump_pathtables_helper (args, DATAP (node), parent_id); + } +} + +void +_vcd_directory_dump_pathtables (VcdDirectory *dir, void *ptl, void *ptm) +{ + _vcd_directory_dump_pathtables_t args; + + vcd_assert (dir != NULL); + + iso9660_pathtable_init (ptl); + iso9660_pathtable_init (ptm); + + args.ptl = ptl; + args.ptm = ptm; + + _vcd_tree_node_traverse_bf (_vcd_tree_root (dir), + traverse_vcd_directory_dump_pathtables, &args); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ |