/*
* This file has been modified for the cdrkit suite.
*
* The behaviour and appearence of the program code below can differ to a major
* extent from the version distributed by the original author(s).
*
* For details, see Changelog file distributed with the cdrkit package. If you
* received this file from another source then ask the distributing person for
* a log of modifications.
*
*/
/* @(#)joliet.c 1.38 05/05/01 joerg */
/*
* File joliet.c - handle Win95/WinNT long file/unicode extensions for iso9660.
*
* Copyright 1997 Eric Youngdale.
* APPLE_HYB James Pearson j.pearson@ge.ucl.ac.uk 22/2/2000
* Copyright (c) 1999,2000,2001 J. Schilling
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Joliet extensions for ISO9660. These are spottily documented by
* Microsoft. In their infinite stupidity, they completely ignored
* the possibility of using an SUSP record with the long filename
* in it, and instead wrote out a duplicate directory tree with the
* long filenames in it.
*
* I am not sure why they did this. One reason is that they get the path
* tables with the long filenames in them.
*
* There are two basic principles to Joliet, and the non-Unicode variant
* known as Romeo. Long filenames seem to be the main one, and the second
* is that the character set and a few other things is substantially relaxed.
*
* The SVD is identical to the PVD, except:
*
* Id is 2, not 1 (indicates SVD).
* escape_sequences contains UCS-2 indicator (levels 1, 2 or 3).
* The root directory record points to a different extent (with different
* size).
* There are different path tables for the two sets of directory trees.
*
* The Unicode level is coded in the SVD as follows:
*
* Standard Level ASCII escape code
* UCS-2 Level-1 %/@
* UCS-2 Level-2 %/C
* UCS-2 Level-3 %/E
*
* The following fields are recorded in Unicode:
* system_id
* volume_id
* volume_set_id
* publisher_id
* preparer_id
* application_id
* copyright_file_id
* abstract_file_id
* bibliographic_file_id
*
* Unicode strings are always encoded in big-endian format.
*
* In a directory record, everything is the same as with iso9660, except
* that the name is recorded in unicode. The name length is specified in
* total bytes, not in number of unicode characters.
*
* The character set used for the names is different with UCS - the
* restrictions are that the following are not allowed:
*
* Characters (00)(00) through (00)(1f) (control chars)
* (00)(2a) '*'
* (00)(2f) '/'
* (00)(3a) ':'
* (00)(3b) ';'
* (00)(3f) '?'
* (00)(5c) '\'
*/
#include <mconfig.h>
#include "genisoimage.h"
#include <timedefs.h>
#include <utypes.h>
#include <intcvt.h>
#include <unls.h> /* For UNICODE translation */
#include <schily.h>
#ifdef USE_ICONV
#include <iconv.h>
#include <errno.h>
#endif
static Uint jpath_table_index;
static struct directory **jpathlist;
static int next_jpath_index = 1;
static int jsort_goof;
static char ucs_codes[] = {
'\0', /* UCS-level 0 is illegal */
'@', /* UCS-level 1 */
'C', /* UCS-level 2 */
'E', /* UCS-level 3 */
};
#ifdef UDF
#ifdef USE_ICONV
size_t
#else
void
#endif
convert_to_unicode(unsigned char *buffer, int size, char *source,
struct unls_table *inls);
int joliet_strlen(const char *string, struct unls_table *inls);
#else
#ifdef USE_ICONV
static size_t
#else
static void
#endif
convert_to_unicode(unsigned char *buffer, int size, char *source,
struct unls_table *inls);
static int joliet_strlen(const char *string, struct nls_table *inls);
#endif
static void get_joliet_vol_desc(struct iso_primary_descriptor *jvol_desc);
static void assign_joliet_directory_addresses(struct directory *node);
static void build_jpathlist(struct directory *node);
static int joliet_compare_paths(void const *r, void const *l);
static int generate_joliet_path_tables(void);
static void generate_one_joliet_directory(struct directory *dpnt,
FILE *outfile);
static int joliet_sort_n_finish(struct directory *this_dir);
static int joliet_compare_dirs(const void *rr, const void *ll);
static int joliet_sort_directory(struct directory_entry **sort_dir);
int joliet_sort_tree(struct directory *node);
static void generate_joliet_directories(struct directory *node, FILE *outfile);
static int jpathtab_write(FILE *outfile);
static int jdirtree_size(int starting_extent);
static int jroot_gen(void);
static int jdirtree_write(FILE *outfile);
static int jvd_write(FILE *outfile);
static int jpathtab_size(int starting_extent);
/*
* conv_charset: convert to/from charsets via Unicode.
*
* Any unknown character is set to '_'
*
*/
unsigned char
conv_charset(unsigned char c,
struct unls_table *inls,
struct unls_table *onls)
{
unsigned char uh;
unsigned char ul;
unsigned char uc;
unsigned char *up;
/* if we have a null mapping, just return the input character */
if (inls == onls)
return (c);
#ifdef USE_ICONV
if(inls->unls_cs2uni == NULL || onls->unls_uni2cs == NULL) {
/*
* This shouldn't be reached
*/
static BOOL iconv_warned = FALSE;
if(!iconv_warned) {
fprintf(stderr, "Warning: Iconv conversion not supported in conv_charset.\n");
iconv_warned = TRUE;
}
return (c);
}
#endif
/* get high and low UNICODE bytes */
uh = inls->unls_cs2uni[c].unls_high;
ul = inls->unls_cs2uni[c].unls_low;
/* get the backconverted page from the output charset */
up = onls->unls_uni2cs[uh];
/* if the page exists, get the backconverted character */
if (up == NULL)
uc = '\0';
else
uc = up[ul];
/* return the backconverted, if it's not NULL */
return (uc ? uc : '_');
}
/*
* Function: convert_to_unicode
*
* Purpose: Perform a unicode conversion on a text string
* using the supplied input character set.
*
* Notes:
*/
#ifdef USE_ICONV
# if UDF
size_t
# else
static size_t
# endif
#else
# if UDF
void
# else
static void
# endif
#endif
convert_to_unicode(unsigned char *buffer, int size, char *source,
struct unls_table *inls)
{
unsigned char *tmpbuf;
int i;
int j;
unsigned char uh,
ul,
uc,
*up;
/*
* If we get a NULL pointer for the source, it means we have an
* inplace copy, and we need to make a temporary working copy first.
*/
if (source == NULL) {
tmpbuf = (Uchar *) e_malloc(size);
memcpy(tmpbuf, buffer, size);
} else {
tmpbuf = (Uchar *) source;
}
#ifdef USE_ICONV
if (inls->iconv_d && inls->unls_cs2uni==NULL &&
inls->unls_uni2cs==NULL) {
char *inptr = tmpbuf;
char *outptr = buffer;
size_t inleft = strlen(tmpbuf);
size_t inlen = inleft;
size_t outleft = size;
iconv(inls->iconv_d, NULL, NULL, NULL, NULL);
if(iconv(inls->iconv_d, &inptr, &inleft, &outptr, &outleft) ==
(size_t)-1 && errno == EILSEQ) {
fprintf(stderr, "Incorrectly encoded string (%s) "
"encountered.\nPossibly creating an invalid "
"Joliet extension. Aborting.\n", source);
exit(1);
}
for (i = 0; (i + 1) < size - outleft; i += 2) { /* Size may be odd!!!*/
if (buffer[i]=='\0') {
switch (buffer[i+1]) { /* Invalid characters for Joliet */
case '*':
case '/':
case ':':
case ';':
case '?':
case '\\':
buffer[i+1]='_';
default:
if (buffer[i+1] == 0x7f ||
buffer[i+1] < 0x20)
buffer[i+1]='_';
}
}
}
if (size & 1) { /* beautification */
buffer[size - 1] = 0;
}
if (source == NULL) {
free(tmpbuf);
}
return (inlen - inleft);
}
#endif
/*
* Now start copying characters. If the size was specified to be 0,
* then assume the input was 0 terminated.
*/
j = 0;
for (i = 0; (i + 1) < size; i += 2, j++) { /* Size may be odd! */
/*
* JS integrated from: Achim_Kaiser@t-online.de
* SGE modified according to Linux kernel source
* Let all valid unicode characters pass
* through (according to charset). Others are set to '_' .
*/
uc = tmpbuf[j]; /* temporary copy */
if (uc != '\0') { /* must be converted */
uh = inls->unls_cs2uni[uc].unls_high; /* convert forward: */
/* hibyte... */
ul = inls->unls_cs2uni[uc].unls_low; /* ...lobyte */
up = inls->unls_uni2cs[uh]; /* convert backward: */
/* page... */
if (up == NULL)
uc = '\0'; /* wrong unicode page */
else
uc = up[ul]; /* backconverted character */
if (uc != tmpbuf[j])
uc = '\0'; /* should be identical */
if (uc <= 0x1f || uc == 0x7f)
uc = '\0'; /* control char */
switch (uc) { /* test special characters */
case '*':
case '/':
case ':':
case ';':
case '?':
case '\\':
case '\0': /* illegal char mark */
/*
* Even Joliet has some standards as to what is
* allowed in a pathname. Pretty tame in
* comparison to what DOS restricts you to.
*/
uc = '_';
}
}
buffer[i] = inls->unls_cs2uni[uc].unls_high; /* final UNICODE */
/* conversion */
buffer[i + 1] = inls->unls_cs2uni[uc].unls_low;
}
if (size & 1) { /* beautification */
buffer[size - 1] = 0;
}
if (source == NULL) {
free(tmpbuf);
}
#ifdef USE_ICONV
return j;
#endif
}
/*
* Function: joliet_strlen
*
* Purpose: Return length in bytes of string after conversion to unicode.
*
* Notes: This is provided mainly as a convenience so that when more
* intelligent Unicode conversion for either Multibyte or 8-bit
* codes is available that we can easily adapt.
*/
#ifdef UDF
int
#else
static int
#endif
joliet_strlen(const char *string, struct unls_table *inls)
{
int rtn;
#ifdef USE_ICONV
if (inls->iconv_d && inls->unls_cs2uni==NULL &&
inls->unls_uni2cs==NULL) {
/*
* we const-cast since we're sure iconv won't change
* the string itself
*/
char *string_ptr = (char *)string;
size_t string_len = strlen(string);
/*
* iconv has no way of finding out the required size
* in the target
*/
char *tmp, *tmp_ptr;
/* we assume that the maximum length is 2 * jlen */
size_t tmp_len = (size_t)jlen * 2 + 1;
tmp = e_malloc(tmp_len);
tmp_ptr = tmp;
iconv(inls->iconv_d, NULL, NULL, NULL, NULL);
iconv(inls->iconv_d, &string_ptr, &string_len, &tmp_ptr,
&tmp_len);
/*
* iconv advanced the tmp pointer with as many chars
* as it has written to it, so we add up the delta
*/
rtn = (tmp_ptr - tmp);
free(tmp);
} else {
rtn = strlen(string) << 1;
}
#else
rtn = strlen(string) << 1;
#endif
/*
* We do clamp the maximum length of a Joliet string to be the
* maximum path size. This helps to ensure that we don't completely
* bolix things up with very long paths. The Joliet specs say that
* the maximum length is 128 bytes, or 64 unicode characters.
*/
if (rtn > 2*jlen) {
rtn = 2*jlen;
}
return (rtn);
}
/*
* Function: get_joliet_vol_desc
*
* Purpose: generate a Joliet compatible volume desc.
*
* Notes: Assume that we have the non-joliet vol desc
* already present in the buffer. Just modifiy the
* appropriate fields.
*/
static void
get_joliet_vol_desc(struct iso_primary_descriptor *jvol_desc)
{
jvol_desc->type[0] = ISO_VD_SUPPLEMENTARY;
jvol_desc->version[0] = 1;
jvol_desc->file_structure_version[0] = 1;
/*
* For now, always do Unicode level 3.
* I don't really know what 1 and 2 are - perhaps a more limited
* Unicode set.
* FIXME(eric) - how does Romeo fit in here?
*/
sprintf(jvol_desc->escape_sequences, "%%/%c", ucs_codes[ucs_level]);
/* Until we have Unicode path tables, leave these unset. */
set_733((char *) jvol_desc->path_table_size, jpath_table_size);
set_731(jvol_desc->type_l_path_table, jpath_table[0]);
set_731(jvol_desc->opt_type_l_path_table, jpath_table[1]);
set_732(jvol_desc->type_m_path_table, jpath_table[2]);
set_732(jvol_desc->opt_type_m_path_table, jpath_table[3]);
/* Set this one up. */
memcpy(jvol_desc->root_directory_record, &jroot_record,
offsetof(struct iso_directory_record, name[0]) + 1);
/*
* Finally, we have a bunch of strings to convert to Unicode.
* FIXME(eric) - I don't know how to do this in general,
* so we will just be really lazy and do a char -> short conversion.
* We probably will want to filter any characters >= 0x80.
*/
convert_to_unicode((Uchar *) jvol_desc->system_id,
sizeof (jvol_desc->system_id), NULL, in_nls);
convert_to_unicode((Uchar *) jvol_desc->volume_id,
sizeof (jvol_desc->volume_id), NULL, in_nls);
convert_to_unicode((Uchar *) jvol_desc->volume_set_id,
sizeof (jvol_desc->volume_set_id), NULL, in_nls);
convert_to_unicode((Uchar *) jvol_desc->publisher_id,
sizeof (jvol_desc->publisher_id), NULL, in_nls);
convert_to_unicode((Uchar *) jvol_desc->preparer_id,
sizeof (jvol_desc->preparer_id), NULL, in_nls);
convert_to_unicode((Uchar *) jvol_desc->application_id,
sizeof (jvol_desc->application_id), NULL, in_nls);
convert_to_unicode((Uchar *) jvol_desc->copyright_file_id,
sizeof (jvol_desc->copyright_file_id), NULL, in_nls);
convert_to_unicode((Uchar *) jvol_desc->abstract_file_id,
sizeof (jvol_desc->abstract_file_id), NULL, in_nls);
convert_to_unicode((Uchar *) jvol_desc->bibliographic_file_id,
sizeof (jvol_desc->bibliographic_file_id), NULL, in_nls);
}
static void
assign_joliet_directory_addresses(struct directory *node)
{
int dir_size;
struct directory *dpnt;
dpnt = node;
while (dpnt) {
if ((dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) == 0) {
/*
* If we already have an extent for this
* (i.e. it came from a multisession disc), then
* don't reassign a new extent.
*/
dpnt->jpath_index = next_jpath_index++;
if (dpnt->jextent == 0) {
dpnt->jextent = last_extent;
dir_size = ISO_BLOCKS(dpnt->jsize);
last_extent += dir_size;
}
}
/* skip if hidden - but not for the rr_moved dir */
if (dpnt->subdir && (!(dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) || dpnt == reloc_dir)) {
assign_joliet_directory_addresses(dpnt->subdir);
}
dpnt = dpnt->next;
}
}
static void
build_jpathlist(struct directory *node)
{
struct directory *dpnt;
dpnt = node;
while (dpnt) {
if ((dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) == 0) {
jpathlist[dpnt->jpath_index] = dpnt;
}
if (dpnt->subdir)
build_jpathlist(dpnt->subdir);
dpnt = dpnt->next;
}
}/* build_jpathlist(... */
static int
joliet_compare_paths(void const *r, void const *l)
{
struct directory const *ll = *(struct directory * const *) l;
struct directory const *rr = *(struct directory * const *) r;
int rparent,
lparent;
char *rpnt,
*lpnt;
unsigned char rtmp[2],
ltmp[2];
struct unls_table *rinls, *linls;
/* make sure root directory is first */
if (rr == root)
return (-1);
if (ll == root)
return (1);
rparent = rr->parent->jpath_index;
lparent = ll->parent->jpath_index;
if (rr->parent == reloc_dir) {
rparent = rr->self->parent_rec->filedir->jpath_index;
}
if (ll->parent == reloc_dir) {
lparent = ll->self->parent_rec->filedir->jpath_index;
}
if (rparent < lparent) {
return (-1);
}
if (rparent > lparent) {
return (1);
}
#ifdef APPLE_HYB
/*
* we may be using the HFS name - so select the correct input
* charset
*/
if (USE_MAC_NAME(rr->self)) {
rpnt = rr->self->hfs_ent->name;
rinls = hfs_inls;
} else {
rpnt = rr->self->name;
rinls = in_nls;
}
if (USE_MAC_NAME(ll->self)) {
lpnt = ll->self->hfs_ent->name;
linls = hfs_inls;
} else {
lpnt = ll->self->name;
linls = in_nls;
}
#else
rpnt = rr->self->name;
lpnt = ll->self->name;
linls = rinls = in_nls;
#endif /* APPLE_HYB */
/* compare the Unicode names */
while (*rpnt && *lpnt) {
#ifdef USE_ICONV
size_t ri, li;
ri = convert_to_unicode(rtmp, 2, rpnt, rinls);
li = convert_to_unicode(ltmp, 2, lpnt, linls);
rpnt += ri;
lpnt += li;
if(!ri && !li)
return (0);
else if(ri && !li)
return (1);
else if(!ri && li)
return (-1);
#else
convert_to_unicode(rtmp, 2, rpnt, rinls);
convert_to_unicode(ltmp, 2, lpnt, linls);
#endif
if (a_to_u_2_byte(rtmp) < a_to_u_2_byte(ltmp))
return (-1);
if (a_to_u_2_byte(rtmp) > a_to_u_2_byte(ltmp))
return (1);
#ifndef USE_ICONV
rpnt++;
lpnt++;
#endif
}
if (*rpnt)
return (1);
if (*lpnt)
return (-1);
return (0);
}/* compare_paths(... */
static int
generate_joliet_path_tables()
{
struct directory_entry *de;
struct directory *dpnt;
int fix;
int j;
int namelen;
char *npnt;
char *npnt1;
int tablesize;
/* First allocate memory for the tables and initialize the memory */
tablesize = jpath_blocks << 11;
jpath_table_m = (char *) e_malloc(tablesize);
jpath_table_l = (char *) e_malloc(tablesize);
memset(jpath_table_l, 0, tablesize);
memset(jpath_table_m, 0, tablesize);
/* Now start filling in the path tables. Start with root directory */
jpath_table_index = 0;
jpathlist = (struct directory **) e_malloc(sizeof (struct directory *)
* next_jpath_index);
memset(jpathlist, 0, sizeof (struct directory *) * next_jpath_index);
build_jpathlist(root);
do {
fix = 0;
#ifdef PROTOTYPES
qsort(&jpathlist[1], next_jpath_index - 1, sizeof (struct directory *),
(int (*) (const void *, const void *)) joliet_compare_paths);
#else
qsort(&jpathlist[1], next_jpath_index - 1, sizeof (struct directory *),
joliet_compare_paths);
#endif
for (j = 1; j < next_jpath_index; j++) {
if (jpathlist[j]->jpath_index != j) {
jpathlist[j]->jpath_index = j;
fix++;
}
}
} while (fix);
for (j = 1; j < next_jpath_index; j++) {
dpnt = jpathlist[j];
if (!dpnt) {
#ifdef USE_LIBSCHILY
comerrno(EX_BAD, "Entry %d not in path tables\n", j);
#else
fprintf(stderr, "Entry %d not in path tables\n", j);
exit(1);
#endif
}
npnt = dpnt->de_name;
npnt1 = strrchr(npnt, PATH_SEPARATOR);
if (npnt1) {
npnt = npnt1 + 1;
}
de = dpnt->self;
if (!de) {
#ifdef USE_LIBSCHILY
comerrno(EX_BAD,
"Fatal Joliet goof - directory has amnesia\n");
#else
fprintf(stderr,
"Fatal Joliet goof - directory has amnesia\n");
exit(1);
#endif
}
#ifdef APPLE_HYB
if (USE_MAC_NAME(de))
namelen = joliet_strlen(de->hfs_ent->name, hfs_inls);
else
#endif /* APPLE_HYB */
namelen = joliet_strlen(de->name, in_nls);
if (dpnt == root) {
jpath_table_l[jpath_table_index] = 1;
jpath_table_m[jpath_table_index] = 1;
} else {
jpath_table_l[jpath_table_index] = namelen;
jpath_table_m[jpath_table_index] = namelen;
}
jpath_table_index += 2;
set_731(jpath_table_l + jpath_table_index, dpnt->jextent);
set_732(jpath_table_m + jpath_table_index, dpnt->jextent);
jpath_table_index += 4;
if (dpnt->parent->jpath_index > 0xffff) {
#ifdef USE_LIBSCHILY
comerrno(EX_BAD,
"Unable to generate sane path tables - too many directories (%d)\n",
dpnt->parent->jpath_index);
#else
fprintf(stderr,
"Unable to generate sane path tables - too many directories (%d)\n",
dpnt->parent->jpath_index);
exit(1);
#endif
}
if (dpnt->parent != reloc_dir) {
set_721(jpath_table_l + jpath_table_index,
dpnt->parent->jpath_index);
set_722(jpath_table_m + jpath_table_index,
dpnt->parent->jpath_index);
} else {
set_721(jpath_table_l + jpath_table_index,
dpnt->self->parent_rec->filedir->jpath_index);
set_722(jpath_table_m + jpath_table_index,
dpnt->self->parent_rec->filedir->jpath_index);
}
jpath_table_index += 2;
/*
* The root directory is still represented in non-unicode
* fashion.
*/
if (dpnt == root) {
jpath_table_l[jpath_table_index] = 0;
jpath_table_m[jpath_table_index] = 0;
jpath_table_index++;
} else {
#ifdef APPLE_HYB
if (USE_MAC_NAME(de)) {
convert_to_unicode((Uchar *) jpath_table_l +
jpath_table_index,
namelen, de->hfs_ent->name, hfs_inls);
convert_to_unicode((Uchar *) jpath_table_m +
jpath_table_index,
namelen, de->hfs_ent->name, hfs_inls);
} else {
#endif /* APPLE_HYB */
convert_to_unicode((Uchar *) jpath_table_l +
jpath_table_index,
namelen, de->name, in_nls);
convert_to_unicode((Uchar *) jpath_table_m +
jpath_table_index,
namelen, de->name, in_nls);
#ifdef APPLE_HYB
}
#endif /* APPLE_HYB */
jpath_table_index += namelen;
}
if (jpath_table_index & 1) {
jpath_table_index++; /* For odd lengths we pad */
}
}
free(jpathlist);
if (jpath_table_index != jpath_table_size) {
#ifdef USE_LIBSCHILY
errmsgno(EX_BAD,
"Joliet path table lengths do not match %d expected: %d\n",
jpath_table_index,
jpath_table_size);
#else
fprintf(stderr,
"Joliet path table lengths do not match %d expected: %d\n",
jpath_table_index,
jpath_table_size);
#endif
}
return (0);
}/* generate_path_tables(... */
static void
generate_one_joliet_directory(struct directory *dpnt, FILE *outfile)
{
unsigned int dir_index;
char *directory_buffer;
int new_reclen;
struct directory_entry *s_entry;
struct directory_entry *s_entry1;
struct iso_directory_record jrec;
unsigned int total_size;
int cvt_len;
struct directory *finddir;
total_size = ISO_ROUND_UP(dpnt->jsize);
directory_buffer = (char *) e_malloc(total_size);
memset(directory_buffer, 0, total_size);
dir_index = 0;
s_entry = dpnt->jcontents;
while (s_entry) {
if (s_entry->de_flags & INHIBIT_JOLIET_ENTRY) {
s_entry = s_entry->jnext;
continue;
}
/*
* If this entry was a directory that was relocated,
* we have a bit of trouble here. We need to dig out the real
* thing and put it back here. In the Joliet tree, there is
* no relocated rock ridge, as there are no depth limits to a
* directory tree.
*/
if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0) {
for (s_entry1 = reloc_dir->contents; s_entry1;
s_entry1 = s_entry1->next) {
if (s_entry1->parent_rec == s_entry) {
break;
}
}
if (s_entry1 == NULL) {
/* We got trouble. */
#ifdef USE_LIBSCHILY
comerrno(EX_BAD,
"Unable to locate relocated directory\n");
#else
fprintf(stderr,
"Unable to locate relocated directory\n");
exit(1);
#endif
}
} else {
s_entry1 = s_entry;
}
/*
* We do not allow directory entries to cross sector
* boundaries. Simply pad, and then start the next entry at
* the next sector
*/
new_reclen = s_entry1->jreclen;
if ((dir_index & (SECTOR_SIZE - 1)) + new_reclen >= SECTOR_SIZE) {
dir_index = ISO_ROUND_UP(dir_index);
}
memcpy(&jrec, &s_entry1->isorec, offsetof(struct iso_directory_record, name[0]));
#ifdef APPLE_HYB
/* Use the HFS name if it exists */
if (USE_MAC_NAME(s_entry1))
cvt_len = joliet_strlen(s_entry1->hfs_ent->name, hfs_inls);
else
#endif /* APPLE_HYB */
cvt_len = joliet_strlen(s_entry1->name, in_nls);
/*
* Fix the record length
* - this was the non-Joliet version we were seeing.
*/
jrec.name_len[0] = cvt_len;
jrec.length[0] = s_entry1->jreclen;
/*
* If this is a directory,
* fix the correct size and extent number.
*/
if ((jrec.flags[0] & ISO_DIRECTORY) != 0) {
if (strcmp(s_entry1->name, ".") == 0) {
jrec.name_len[0] = 1;
set_733((char *) jrec.extent, dpnt->jextent);
set_733((char *) jrec.size, ISO_ROUND_UP(dpnt->jsize));
} else if (strcmp(s_entry1->name, "..") == 0) {
jrec.name_len[0] = 1;
if (dpnt->parent == reloc_dir) {
set_733((char *)jrec.extent, dpnt->self->parent_rec->filedir->jextent);
set_733((char *)jrec.size, ISO_ROUND_UP(dpnt->self->parent_rec->filedir->jsize));
} else {
set_733((char *)jrec.extent, dpnt->parent->jextent);
set_733((char *)jrec.size, ISO_ROUND_UP(dpnt->parent->jsize));
}
} else {
if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0) {
finddir = reloc_dir->subdir;
} else {
finddir = dpnt->subdir;
}
while (1 == 1) {
if (finddir->self == s_entry1)
break;
finddir = finddir->next;
if (!finddir) {
#ifdef USE_LIBSCHILY
comerrno(EX_BAD, "Fatal goof - unable to find directory location\n");
#else
fprintf(stderr, "Fatal goof - unable to find directory location\n");
exit(1);
#endif
}
}
set_733((char *)jrec.extent, finddir->jextent);
set_733((char *)jrec.size,
ISO_ROUND_UP(finddir->jsize));
}
}
memcpy(directory_buffer + dir_index, &jrec,
offsetof(struct iso_directory_record, name[0]));
dir_index += offsetof(struct iso_directory_record, name[0]);
/*
* Finally dump the Unicode version of the filename.
* Note - . and .. are the same as with non-Joliet discs.
*/
if ((jrec.flags[0] & ISO_DIRECTORY) != 0 &&
strcmp(s_entry1->name, ".") == 0) {
directory_buffer[dir_index++] = 0;
} else if ((jrec.flags[0] & ISO_DIRECTORY) != 0 &&
strcmp(s_entry1->name, "..") == 0) {
directory_buffer[dir_index++] = 1;
} else {
#ifdef APPLE_HYB
if (USE_MAC_NAME(s_entry1)) {
/* Use the HFS name if it exists */
convert_to_unicode(
(Uchar *) directory_buffer+dir_index,
cvt_len,
s_entry1->hfs_ent->name, hfs_inls);
} else
#endif /* APPLE_HYB */
{
convert_to_unicode(
(Uchar *) directory_buffer+dir_index,
cvt_len,
s_entry1->name, in_nls);
}
dir_index += cvt_len;
}
if (dir_index & 1) {
directory_buffer[dir_index++] = 0;
}
s_entry = s_entry->jnext;
}
if (dpnt->jsize != dir_index) {
#ifdef USE_LIBSCHILY
errmsgno(EX_BAD,
"Unexpected joliet directory length %d expected: %d '%s'\n",
dpnt->jsize,
dir_index, dpnt->de_name);
#else
fprintf(stderr,
"Unexpected joliet directory length %d expected: %d '%s'\n",
dpnt->jsize,
dir_index, dpnt->de_name);
#endif
}
jtwrite(directory_buffer, total_size, 1, 0, FALSE);
xfwrite(directory_buffer, total_size, 1, outfile, 0, FALSE);
last_extent_written += total_size >> 11;
free(directory_buffer);
}/* generate_one_joliet_directory(... */
static int
joliet_sort_n_finish(struct directory *this_dir)
{
struct directory_entry *s_entry;
int status = 0;
/*
* don't want to skip this directory if it's the reloc_dir
* at the moment
*/
if (this_dir != reloc_dir &&
this_dir->dir_flags & INHIBIT_JOLIET_ENTRY) {
return (0);
}
for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) {
/* skip hidden entries */
if ((s_entry->de_flags & INHIBIT_JOLIET_ENTRY) != 0) {
continue;
}
/*
* First update the path table sizes for directories.
*
* Finally, set the length of the directory entry if Joliet is
* used. The name is longer, but no Rock Ridge is ever used
* here, so depending upon the options the entry size might
* turn out to be about the same. The Unicode name is always
* a multiple of 2 bytes, so we always add 1 to make it an
* even number.
*/
if (s_entry->isorec.flags[0] & ISO_DIRECTORY) {
if (strcmp(s_entry->name, ".") != 0 &&
strcmp(s_entry->name, "..") != 0) {
#ifdef APPLE_HYB
if (USE_MAC_NAME(s_entry))
/* Use the HFS name if it exists */
jpath_table_size +=
joliet_strlen(s_entry->hfs_ent->name, hfs_inls) +
offsetof(struct iso_path_table, name[0]);
else
#endif /* APPLE_HYB */
jpath_table_size +=
joliet_strlen(s_entry->name, in_nls) +
offsetof(struct iso_path_table, name[0]);
if (jpath_table_size & 1) {
jpath_table_size++;
}
} else {
if (this_dir == root &&
strlen(s_entry->name) == 1) {
jpath_table_size += 1 + offsetof(struct iso_path_table, name[0]);
if (jpath_table_size & 1)
jpath_table_size++;
}
}
}
if (strcmp(s_entry->name, ".") != 0 &&
strcmp(s_entry->name, "..") != 0) {
#ifdef APPLE_HYB
if (USE_MAC_NAME(s_entry))
/* Use the HFS name if it exists */
s_entry->jreclen =
offsetof(struct iso_directory_record, name[0])
+ joliet_strlen(s_entry->hfs_ent->name, hfs_inls)
+ 1;
else
#endif /* APPLE_HYB */
s_entry->jreclen =
offsetof(struct iso_directory_record, name[0])
+ joliet_strlen(s_entry->name, in_nls)
+ 1;
} else {
/*
* Special - for '.' and '..' we generate the same
* records we did for non-Joliet discs.
*/
s_entry->jreclen =
offsetof(struct iso_directory_record, name[0])
+ 1;
}
}
if ((this_dir->dir_flags & INHIBIT_JOLIET_ENTRY) != 0) {
return (0);
}
this_dir->jcontents = this_dir->contents;
status = joliet_sort_directory(&this_dir->jcontents);
/*
* Now go through the directory and figure out how large this one will
* be. Do not split a directory entry across a sector boundary
*/
s_entry = this_dir->jcontents;
/*
* XXX Is it ok to comment this out?
*/
/*XXX JS this_dir->ce_bytes = 0;*/
for (s_entry = this_dir->jcontents; s_entry;
s_entry = s_entry->jnext) {
int jreclen;
if ((s_entry->de_flags & INHIBIT_JOLIET_ENTRY) != 0) {
continue;
}
jreclen = s_entry->jreclen;
if ((this_dir->jsize & (SECTOR_SIZE - 1)) + jreclen >=
SECTOR_SIZE) {
this_dir->jsize = ISO_ROUND_UP(this_dir->jsize);
}
this_dir->jsize += jreclen;
}
return (status);
}
/*
* Similar to the iso9660 case,
* except here we perform a full sort based upon the
* regular name of the file, not the 8.3 version.
*/
static int
joliet_compare_dirs(const void *rr, const void *ll)
{
char *rpnt,
*lpnt;
struct directory_entry **r,
**l;
unsigned char rtmp[2],
ltmp[2];
struct unls_table *linls, *rinls;
r = (struct directory_entry **) rr;
l = (struct directory_entry **) ll;
#ifdef APPLE_HYB
/*
* we may be using the HFS name - so select the correct input
* charset
*/
if (USE_MAC_NAME(*r)) {
rpnt = (*r)->hfs_ent->name;
rinls = hfs_inls;
} else {
rpnt = (*r)->name;
rinls = in_nls;
}
if (USE_MAC_NAME(*l)) {
lpnt = (*l)->hfs_ent->name;
linls = hfs_inls;
} else {
lpnt = (*l)->name;
linls = in_nls;
}
#else
rpnt = (*r)->name;
lpnt = (*l)->name;
rinls = linls = in_nls;
#endif /* APPLE_HYB */
/*
* If the entries are the same, this is an error.
* Joliet specs allow for a maximum of 64 characters.
*/
if (strncmp(rpnt, lpnt, jlen) == 0) {
#ifdef USE_LIBSCHILY
errmsgno(EX_BAD,
"Error: %s and %s have the same Joliet name\n",
(*r)->whole_name, (*l)->whole_name);
#else
fprintf(stderr,
"Error: %s and %s have the same Joliet name\n",
(*r)->whole_name, (*l)->whole_name);
#endif
jsort_goof++;
}
/*
* Put the '.' and '..' entries on the head of the sorted list.
* For normal ASCII, this always happens to be the case, but out of
* band characters cause this not to be the case sometimes.
*/
if (strcmp(rpnt, ".") == 0)
return (-1);
if (strcmp(lpnt, ".") == 0)
return (1);
if (strcmp(rpnt, "..") == 0)
return (-1);
if (strcmp(lpnt, "..") == 0)
return (1);
#ifdef DVD_VIDEO
/*
* There're rumors claiming that some players assume VIDEO_TS.IFO
* to be the first file in VIDEO_TS/ catalog. Well, it's basically
* the only file a player has to actually look for, as the whole
* video content can be "rolled down" from this file alone.
* <appro@fy.chalmers.se>
*/
/*
* XXX This code has to be moved from the Joliet implementation
* XXX to the UDF implementation if we implement decent UDF support
* XXX with a separate name space for the UDF file tree.
*/
if (dvd_video) {
if (strcmp(rpnt, "VIDEO_TS.IFO") == 0)
return (-1);
if (strcmp(lpnt, "VIDEO_TS.IFO") == 0)
return (1);
}
#endif
while (*rpnt && *lpnt) {
#ifdef USE_ICONV
size_t ri, li;
#endif
if (*rpnt == ';' && *lpnt != ';')
return (-1);
if (*rpnt != ';' && *lpnt == ';')
return (1);
if (*rpnt == ';' && *lpnt == ';')
return (0);
/*
* Extensions are not special here.
* Don't treat the dot as something that must be bumped to
* the start of the list.
*/
#if 0
if (*rpnt == '.' && *lpnt != '.')
return (-1);
if (*rpnt != '.' && *lpnt == '.')
return (1);
#endif
#ifdef USE_ICONV
ri = convert_to_unicode(rtmp, 2, rpnt, rinls);
li = convert_to_unicode(ltmp, 2, lpnt, linls);
rpnt += ri;
lpnt += li;
if(!ri && !li)
return (0);
else if(ri && !li)
return (1);
else if(!ri && li)
return (-1);
#else
convert_to_unicode(rtmp, 2, rpnt, rinls);
convert_to_unicode(ltmp, 2, lpnt, linls);
#endif
if (a_to_u_2_byte(rtmp) < a_to_u_2_byte(ltmp))
return (-1);
if (a_to_u_2_byte(rtmp) > a_to_u_2_byte(ltmp))
return (1);
#ifndef USE_ICONV
rpnt++;
lpnt++;
#endif
}
if (*rpnt)
return (1);
if (*lpnt)
return (-1);
return (0);
}
/*
* Function: sort_directory
*
* Purpose: Sort the directory in the appropriate ISO9660
* order.
*
* Notes: Returns 0 if OK, returns > 0 if an error occurred.
*/
static int
joliet_sort_directory(struct directory_entry **sort_dir)
{
int dcount = 0;
int i;
struct directory_entry *s_entry;
struct directory_entry **sortlist;
s_entry = *sort_dir;
while (s_entry) {
/* skip hidden entries */
if (!(s_entry->de_flags & INHIBIT_JOLIET_ENTRY))
dcount++;
s_entry = s_entry->next;
}
/* OK, now we know how many there are. Build a vector for sorting. */
sortlist = (struct directory_entry **)
e_malloc(sizeof (struct directory_entry *) * dcount);
dcount = 0;
s_entry = *sort_dir;
while (s_entry) {
/* skip hidden entries */
if (!(s_entry->de_flags & INHIBIT_JOLIET_ENTRY)) {
sortlist[dcount] = s_entry;
dcount++;
}
s_entry = s_entry->next;
}
jsort_goof = 0;
#ifdef PROTOTYPES
qsort(sortlist, dcount, sizeof (struct directory_entry *),
(int (*) (const void *, const void *)) joliet_compare_dirs);
#else
qsort(sortlist, dcount, sizeof (struct directory_entry *),
joliet_compare_dirs);
#endif
/* Now reassemble the linked list in the proper sorted order */
for (i = 0; i < dcount - 1; i++) {
sortlist[i]->jnext = sortlist[i + 1];
}
sortlist[dcount - 1]->jnext = NULL;
*sort_dir = sortlist[0];
free(sortlist);
return (jsort_goof);
}
int
joliet_sort_tree(struct directory *node)
{
struct directory *dpnt;
int ret = 0;
dpnt = node;
while (dpnt) {
ret = joliet_sort_n_finish(dpnt);
if (ret) {
break;
}
if (dpnt->subdir)
ret = joliet_sort_tree(dpnt->subdir);
if (ret) {
break;
}
dpnt = dpnt->next;
}
return (ret);
}
static void
generate_joliet_directories(struct directory *node, FILE *outfile)
{
struct directory *dpnt;
dpnt = node;
while (dpnt) {
if ((dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) == 0) {
/*
* In theory we should never reuse a directory, so this
* doesn't make much sense.
*/
if (dpnt->jextent > session_start) {
generate_one_joliet_directory(dpnt, outfile);
}
}
/* skip if hidden - but not for the rr_moved dir */
if (dpnt->subdir &&
(!(dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) ||
dpnt == reloc_dir)) {
generate_joliet_directories(dpnt->subdir, outfile);
}
dpnt = dpnt->next;
}
}
/*
* Function to write the EVD for the disc.
*/
static int
jpathtab_write(FILE *outfile)
{
/* Next we write the path tables */
jtwrite(jpath_table_l, jpath_blocks << 11, 1, 0, FALSE);
xfwrite(jpath_table_l, jpath_blocks << 11, 1, outfile, 0, FALSE);
last_extent_written += jpath_blocks;
jtwrite(jpath_table_m, jpath_blocks << 11, 1, 0, FALSE);
xfwrite(jpath_table_m, jpath_blocks << 11, 1, outfile, 0, FALSE);
last_extent_written += jpath_blocks;
free(jpath_table_l);
free(jpath_table_m);
jpath_table_l = NULL;
jpath_table_m = NULL;
return (0);
}
static int
jdirtree_size(int starting_extent)
{
assign_joliet_directory_addresses(root);
return (0);
}
static int
jroot_gen()
{
jroot_record.length[0] =
1 + offsetof(struct iso_directory_record, name[0]);
jroot_record.ext_attr_length[0] = 0;
set_733((char *) jroot_record.extent, root->jextent);
set_733((char *) jroot_record.size, ISO_ROUND_UP(root->jsize));
iso9660_date(jroot_record.date, root_statbuf.st_mtime);
jroot_record.flags[0] = ISO_DIRECTORY;
jroot_record.file_unit_size[0] = 0;
jroot_record.interleave[0] = 0;
set_723(jroot_record.volume_sequence_number, volume_sequence_number);
jroot_record.name_len[0] = 1;
return (0);
}
static int
jdirtree_write(FILE *outfile)
{
generate_joliet_directories(root, outfile);
return (0);
}
/*
* Function to write the EVD for the disc.
*/
static int
jvd_write(FILE *outfile)
{
struct iso_primary_descriptor jvol_desc;
/* Next we write out the boot volume descriptor for the disc */
jvol_desc = vol_desc;
get_joliet_vol_desc(&jvol_desc);
jtwrite(&jvol_desc, SECTOR_SIZE, 1, 0, FALSE);
xfwrite(&jvol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
last_extent_written++;
return (0);
}
/*
* Functions to describe padding block at the start of the disc.
*/
static int
jpathtab_size(int starting_extent)
{
jpath_table[0] = starting_extent;
jpath_table[1] = 0;
jpath_table[2] = jpath_table[0] + jpath_blocks;
jpath_table[3] = 0;
last_extent += 2 * jpath_blocks;
return (0);
}
struct output_fragment joliet_desc = {NULL, oneblock_size, jroot_gen, jvd_write, "Joliet Volume Descriptor" };
struct output_fragment jpathtable_desc = {NULL, jpathtab_size, generate_joliet_path_tables, jpathtab_write, "Joliet path table" };
struct output_fragment jdirtree_desc = {NULL, jdirtree_size, NULL, jdirtree_write, "Joliet directory tree" };
syntax highlighted by Code2HTML, v. 0.9.1