libcdio-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Libcdio-devel] [PATCH] Add UDF Unicode support


From: Pete Batard
Subject: [Libcdio-devel] [PATCH] Add UDF Unicode support
Date: Thu, 20 Feb 2014 02:27:44 +0000
User-agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:24.0) Gecko/20100101 Thunderbird/24.3.0

Hi,

Having recently found that the unicode16_decode() call we use in udf_fs isn't actually doing any actual Unicode decoding (just strips the MSB from UTF-16), please find attached a patch that adds Unicode support to udf_fs and will ensure we always return UTF-8 strings where relevant.

The patch also contains an associated test as well as the required UDF image (which was created using ImgBurn).

Regards,

/Pete
>From 6310430dd712e9769e49dd942d2200cb27951cec Mon Sep 17 00:00:00 2001
From: Pete Batard <address@hidden>
Date: Thu, 20 Feb 2014 02:21:27 +0000
Subject: [PATCH] Add Unicode support for UDF

---
 lib/driver/utf8.c      |   3 --
 lib/udf/udf_fs.c       | 104 ++++++++++++++++++++++++++++++++-----------------
 test/check_fuzzyiso.sh |   2 +-
 test/data/udf102.iso   | Bin 784384 -> 1245184 bytes
 test/testudf.c.in      |  32 +++++++++++----
 5 files changed, 94 insertions(+), 47 deletions(-)

diff --git a/lib/driver/utf8.c b/lib/driver/utf8.c
index db68d5b..b94b833 100644
--- a/lib/driver/utf8.c
+++ b/lib/driver/utf8.c
@@ -113,7 +113,6 @@ FILE* fopen_utf8(const char* filename, const char* mode)
 }
 #endif
 
-#ifdef HAVE_JOLIET
 #ifdef HAVE_ICONV
 #include <iconv.h>
 struct cdio_charset_coverter_s
@@ -344,5 +343,3 @@ bool cdio_charset_to_utf8(const char *src, size_t src_len, 
cdio_utf8_t **dst,
   return (*dst != NULL);
 }
 #endif /* HAVE_ICONV */
-
-#endif /* HAVE_JOLIET */
diff --git a/lib/udf/udf_fs.c b/lib/udf/udf_fs.c
index 81719b6..e21571a 100644
--- a/lib/udf/udf_fs.c
+++ b/lib/udf/udf_fs.c
@@ -70,6 +70,9 @@ const char VSD_STD_ID_NSR03[] = {'N', 'S', 'R', '0', '3'};
 const char VSD_STD_ID_TEA01[] = {'T', 'E', 'A', '0', '1'};
 
 #include <cdio/bytesex.h>
+#include <cdio/utf8.h>
+#include <cdio/util.h>
+
 #include "udf_private.h"
 #include "udf_fs.h"
 #include "cdio_assert.h"
@@ -274,23 +277,32 @@ udf_fopen(udf_dirent_t *p_udf_root, const char *psz_name)
   return p_udf_file;
 }
 
-/* Convert unicode16 to 8-bit char by dripping MSB.
-   Wonder if iconv can be used here
+/* Convert unicode16 to UTF-8.
+   The returned string is allocated and must be freed by the caller
 */
-static int
-unicode16_decode( const uint8_t *data, int i_len, char *target )
+static char*
+unicode16_decode(const uint8_t *data, int i_len)
 {
-  int p = 1, i = 0;
-
-  if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do {
-    if( data[ 0 ] == 16 ) p++;  /* Ignore MSB of unicode16 */
-    if( p < i_len ) {
-      target[ i++ ] = data[ p++ ];
-    }
-  } while( p < i_len );
+  int i;
+  char* r = NULL;
 
-  target[ i ] = '\0';
-  return 0;
+  switch (data[0])
+  {
+  case 8:
+    r = (char*)calloc(i_len, 1);
+    if (r == NULL)
+      return r;
+    for (i=0; i<i_len-1; i++)
+      r[i] = data[i+1];
+    return r;
+  case 16:
+    cdio_charset_to_utf8((char*)&data[1], i_len-1, &r, "UCS-2BE");
+    return r;
+  default:
+    /* Empty string, as some existing sections can't take a NULL pointer */ 
+    r = (char*)calloc(1, 1);
+    return r;
+  }
 }
 
 
@@ -431,18 +443,24 @@ udf_open (const char *psz_path)
 }
 
 /**
- * Gets the Volume Identifier string, in 8bit unicode (latin-1)
+ * Gets the Volume Identifier, as an UTF-8 string
  * psz_volid, place to put the string
  * i_volid, size of the buffer psz_volid points to
  * returns the size of buffer needed for all data
+ * Note: this call accepts a NULL psz_volid, to retrieve the length required.
  */
-int
-udf_get_volume_id(udf_t *p_udf, /*out*/ char *psz_volid,  unsigned int i_volid)
+int 
+udf_get_volume_id(udf_t *p_udf, /*out*/ char *psz_volid, unsigned int i_volid)
 {
   uint8_t data[UDF_BLOCKSIZE];
   const udf_pvd_t *p_pvd = (udf_pvd_t *) &data;
+  char* r;
   unsigned int volid_len;
 
+  /* clear the output to empty string */
+  if (psz_volid != NULL)
+    psz_volid[0] = 0;
+
   /* get primary volume descriptor */
   if ( DRIVER_OP_SUCCESS != udf_read_sectors(p_udf, &data, p_udf->pvd_lba, 1) )
     return 0;
@@ -452,10 +470,17 @@ udf_get_volume_id(udf_t *p_udf, /*out*/ char *psz_volid,  
unsigned int i_volid)
     /* this field is only UDF_VOLID_SIZE bytes something is wrong */
     volid_len = UDF_VOLID_SIZE-1;
   }
-  if(i_volid > volid_len) {
-    i_volid = volid_len;
+
+  r = unicode16_decode((uint8_t *) p_pvd->vol_ident, volid_len);
+  if (r == NULL)
+    return 0;
+
+  volid_len = strlen(r)+1;     /* +1 for NUL terminator */
+  if (psz_volid != NULL) {
+    strncpy(psz_volid, r, MIN(volid_len, i_volid));
+    psz_volid[i_volid-1] = 0;  /* strncpy does not always terminate the dest */
   }
-  unicode16_decode((uint8_t *) p_pvd->vol_ident, i_volid, psz_volid);
+  free(r);
 
   return volid_len;
 }
@@ -490,27 +515,39 @@ udf_get_volumeset_id(udf_t *p_udf, /*out*/ uint8_t 
*volsetid,
 }
 
 /**
- * Gets the Logical Volume Identifier string, in 8bit unicode (latin-1)
+ * Gets the Logical Volume Identifier string, as an UTF-8 string
  * psz_logvolid, place to put the string (should be at least 64 bytes)
  * i_logvolid, size of the buffer psz_logvolid points to
- * returns the size of buffer needed for all data
+ * returns the size of buffer needed for all data, including NUL terminator
  * A call to udf_get_root() should have been issued before this call
+ * Note: this call accepts a NULL psz_volid, to retrieve the length required.
  */
 int
-udf_get_logical_volume_id(udf_t *p_udf, /*out*/ char *psz_logvolid,  unsigned 
int i_logvolid)
+udf_get_logical_volume_id(udf_t *p_udf, /*out*/ char *psz_logvolid, unsigned 
int i_logvolid)
 {
   uint8_t data[UDF_BLOCKSIZE];
   logical_vol_desc_t *p_logvol = (logical_vol_desc_t *) &data;
+  char* r;
   int logvolid_len;
 
-  if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, p_logvol, p_udf->lvd_lba, 
1) )
+  /* clear the output to empty string */
+  if (psz_logvolid != NULL)
+    psz_logvolid[0] = 0;
+
+  if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, p_logvol, p_udf->lvd_lba, 
1) ) 
     return 0;
 
-  logvolid_len = (p_logvol->logvol_id[127]+1)/2;
-  if (i_logvolid > logvolid_len)
-    i_logvolid = logvolid_len;
+  r = unicode16_decode((uint8_t *) p_logvol->logvol_id, 
p_logvol->logvol_id[127]);
+  if (r == NULL)
+    return 0;
+
+  logvolid_len = strlen(r)+1;  /* +1 for NUL terminator */
+  if (psz_logvolid != NULL) {
+    strncpy(psz_logvolid, r, MIN(logvolid_len, i_logvolid));
+    psz_logvolid[i_logvolid-1] = 0;    /* strncpy does not always terminate 
the dest */
+  }
+  free(r);
 
-  unicode16_decode((uint8_t *) p_logvol->logvol_id, 2*i_logvolid, 
psz_logvolid);
   return logvolid_len;
 }
 
@@ -651,6 +688,7 @@ udf_dirent_t *
 udf_readdir(udf_dirent_t *p_udf_dirent)
 {
   udf_t *p_udf;
+  uint8_t* p;
 
   if (p_udf_dirent->dir_left <= 0) {
     udf_dirent_free(p_udf_dirent);
@@ -710,13 +748,9 @@ udf_readdir(udf_dirent_t *p_udf_dirent)
                return NULL;
        }
 
-       if (strlen(p_udf_dirent->psz_name) < i_len)
-         p_udf_dirent->psz_name = (char *)
-           realloc(p_udf_dirent->psz_name, sizeof(char)*i_len+1);
-
-       unicode16_decode(p_udf_dirent->fid->u.imp_use.data
-                        + p_udf_dirent->fid->u.i_imp_use,
-                        i_len, p_udf_dirent->psz_name);
+       free_and_null(p_udf_dirent->psz_name);
+       p = (uint8_t*)p_udf_dirent->fid->u.imp_use.data + 
p_udf_dirent->fid->u.i_imp_use;
+       p_udf_dirent->psz_name = unicode16_decode(p, i_len);
       }
       return p_udf_dirent;
     }
diff --git a/test/check_fuzzyiso.sh b/test/check_fuzzyiso.sh
index 0eb71d9..cef7a5a 100755
--- a/test/check_fuzzyiso.sh
+++ b/test/check_fuzzyiso.sh
@@ -22,7 +22,7 @@ fi
 cd $srcdir; src_dir=`pwd`
 for file in $src_dir/data/*.bin $src_dir/data/*.iso $src_dir/data/*.nrg ; do 
   case "$file" in
-  $src_dir/data/p1.nrg | $src_dir/data/p1.bin | $src_dir/data/cdda.bin | 
$src_dir/data/cdda-mcn.nrg | $src_dir/data/svcdgs.nrg | 
$src_dir/data/cdtext.bin )
+  $src_dir/data/p1.nrg | $src_dir/data/p1.bin | $src_dir/data/cdda.bin | 
$src_dir/data/cdda-mcn.nrg | $src_dir/data/svcdgs.nrg | 
$src_dir/data/cdtext.bin | $src_dir/data/udf102.iso )
     good_rc=1
     ;;
   *) 
diff --git a/test/data/udf102.iso b/test/data/udf102.iso
index 
9e088313db7e397052e6a764757643078a41c8f7..2639d0d451f920c69511b6a58bc453a3b05b03b1
 100644
GIT binary patch
delta 1545
zcmd5+O-vI}5T4mxwvUZ9+q7V*2B}CiMoVca6fQy&address@hidden&Hnvf>m
address@hidden@`7^V$bcw~pgoE?)_Px%VdEd-8Z(4BL
z3x&address@hidden)2qKYPq+7FmlSl7O08|$KbQpr7plUOu-e%(bpqEBp-zal9HzhoitK3
z<B*|mKdqIKAbocU&tp`^)1prb#G%eZCniQSH>R$(OebQgSUMK>+|address@hidden
zsMy+gUAF{k`g_8h>6M1S{RpyMU^ehaxFsCNU<oHg)m!S8N#3pGJ#0(E#Aksa1wd!K
zs#Vfwg;Dc=A*)address@hidden&=~XzJ{6DBz!p&v;k1Ug_m{{p1z!0
zp#$dKoBDsvd;J<kJk#r7hx7}K{yX*cNxERzb}%9RCO-WO`#;H$*-sL4+s^)7V!!Ps
zA;{)w=;PveUYlDi){z8CxYsg;Hb1Bc|E6a$ci~o~m{*E<wV2n6`MP3WFXnx=>C9~Z
address@hidden)?7}MI)E6UlRa|obM5BsM<K7TL)1S
zKL`%M!RM2+w>8_$saBuRMn96)*|$uMzb+arWXB6h3;Zg}!%+FvllGE(R{iZIdyD<t
zwb{mvzNH$ph3O885_BTs10fo(gc}3vc`^pYPOW=fXbtbkB6~3m+>vDKx+?N(Rs>V8
zN%45?aQO(Rc{idbW0o2nXyc-f(;s_7-izm!EBU%BrC+!1Sv!4#cZKP6g=-fuLXFY>
z`f~bBJ5EMp-u>L<Y=bBNT}LW^DqbGA7R&>aU*Unl>X-*3-jPUlrn<V*JvM&NT1!GR
R+Q|V|9UM_!>=fU8zX2rRFfafB

literal 784384
zcmeI*Yj<4Nec$l`Em8Eusg%^YDA!{;jz}4zNXn8$Q*k^5h9qnfpmCAHUOfN~h_Qef
zWCkFG-c}yx9rOh>t6g2oD{0?AS6)E-AXzIfp!YUy)%(address@hidden
z00(Ez+559E|GoFw1GHLvAb<address@hidden<address@hidden
zfB*srAb<address@hidden<address@hidden<address@hidden
z0tg_000IagfB*srAb<address@hidden<address@hidden
zAb<address@hidden|s;address@hidden&address@hidden|L^)!gq?-#_WIPMh<;
z*12=7b~>r;1L<S#j6a`mJ)N%hbnBeIKiAql`~0)7x6bLO>address@hidden
address@hidden)hn;Q`r2iEoO|qmkIsIyX20>*uDE<4fB*uY
z1c5U-{<n(v{IAtoZgpE5N^$*pldM$n|0^$dC&%i(P&YW|f3xxbb2^Yd=AQEB^R2US
zWbW*f;(x0?H9xGQt<address@hidden)Edg+M(apl4O)Hud!X-ETPv-*AG6N~^Z!7J_&_H;
address@hidden&>E;(S5?0R(=;0)JA){~vKWxIF?0Ab<address@hidden>cKQZge|d|K|e%1Q0*~
z0R%on0?TWwm#-54KSL!>RuMn|0R#|ewbq&cQw<P6009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL__G$6Yn^HR?bclD_x|ABxnkzx
z&rSZ|@65sJm(address@hidden|8ju;oqOf-;<c~ey!zUL{=KfRr|14KzWRyQ%G!FTSQzdf
zjLPx2+%7f-{jK44`GIf#QD6UoZ<J1a<GDY4=Iry&Ui*T-eX(address@hidden
address@hidden@P<(pcgk5Zx(mnES3g4vjOSMT#iQ{F;>3M
z*y7J9XHUG$Gf(`<$MFMV`^TZmH4#7n0R%pxz|)HV&address@hidden&=XI?!`WIt>uf`t+
address@hidden<G#fcVl1=gkI$bgBY*$`2z<H)&MM+Rqlkaz#UIRlSy}m<address@hidden
z-=9++UW`n5A;S*^_WHfSp|f^oE^?>`7XEJfaaLcZwU1BuTKsdZ*0-G2vys|q2KWjA
address@hidden<=4P~_Wx_FU^%^tz`$#A`Vf0R#|0009I(odV|+|address@hidden
zsYU<+1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0;r6DOd*{L}h-`u)address@hidden
zzt{Ek%(Z?9U;address@hidden@avxN_X}Gs{(|address@hidden
z4=C`IVt8LseEpB#ox7sfq`!gZKbv>w&0=Y=Gn~23nYkQ~KJ9-Wa3)V5`<KM_kDZts
zAb<address@hidden>frxpJ%D(c_+!Mk%;_3|_N7h{00F0ZX#_K{EKhtsG<h}Mrgc~6M3%$`4)
zWk;$IKmY**e)Ixo74d&Y5&z7KKbU((address@hidden<^l6{ppHm)Qj7)eTV_~>|u-ESm4xP0#
zbCE+mu<&=&kF)wRt$lQEcRP#4aygn^`p{25A)|@sTCHz6t!E>(`ic((5I_I{1U|gL
address@hidden@4Iuae#+8LauGlP0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#~EqzZgq
zzyJ5=^!tC$-Fp~+S7!F_($D^dnJ>Oi27K<Ne9i~?n|was|IW<$zx+ZzA%Fk^2q1vK
address@hidden|address@hidden|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~
z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**
z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0
z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{
z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL
zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0;L|E_rgf(EPg--Wxj%YW
z|EFC1R;yJwGx~hlpZP!l0R#|00D;B{%(b3Y)OX<L0|5jOKmY**J}Ux_{But;0Qjtw
zH~B;W0R#|0zyfovv&{dg3J4&800Ibn#sp&if0p_GXRO%CE&>Q3fWT*5;Cwv)k7oeq
zOmVKYd-nNfUvHf|address@hidden<brmu1H_U>H!%*(&v3wm+k^9w8Y
z?k_Fhx_-q!<pTi(5I_I{1b);4=VSd}LHevhzdq{Xzn>7i#~E{9d!P9KldXl;O6y+h
zeru_<+`84e-eMqNQ9ckr009IL_>address@hidden;WJtQxb_8o{W<;T
zgYg6Z&>t&n>address@hidden;hTCQO;rsvpa~c73TO03x;>UBwGaqyMpZUni
z)+p?R_lp6(v3N7k_xioTq3?0##7Q6du3P{C1Q0;rGbZq~;{U%;#ChTA-<`yrr(~e;
z;!pbcQtMCh$KUzlG&X%Nem4DncHHo!uWLiri=Eru&SJ4#j)address@hidden>GSvW
zPmd<}KmY**5I_Kdk03Co`2P<T?c2Zovk_PIl^+63#sbx)ecmtX=-<70A)nWJ*5`e$
zIw3wkcWR~o#1Huf0R#|0009I(Ap)NN|0BiqfBf3-PGWhU|address@hidden;~pY!24
ze{A~G_fPuzPc#4j$r&fl|9#)QFfsEJzC-{41Q0*~fe$0_`Og>nxV5}d-0CiOS37r#
zdmA_IEG-oJ*Ii!gw$lkZ=go37?hgmW)%oJvM}xAsa_!occ3V%S4~|Fu2fK&address@hidden
z`1-Z^_)&3lRF=isaOdz*Z&VgHhew0$-k~lqUn~u_F13Bz;?--#dbxkFR~Gm7dRygu
zv3AryEQ?oOxjbLo7>*Bp_T5fVT)ukc%9WR{ymI;LMX|BgX&2px<>address@hidden
zA0BEi!r`#kQgVvkV7u7vj}J%v%_Ch-=WgoO`~G3S9Jh<%PI0)aSMK$<%E7oSwuf6s
z`{m$pzR>x_)^2a`pg(v}^bdXc!SJx??d=U8mD`uv;s~&Nqq4WZxmOCHSl=zHD~*et
z;i%YG%8PN8bLW1$9QPj#QVI`yZ<address@hidden|!%IbR
z<5=k)9FBTprSDMpioz-f<*2t;+&kJ-Cfav$MwHzC;IJHQr&~QZ>W#EtK;__D74LN`
z|Eyi5`K6bXnti8pe54flrqz!u;&address@hidden)address@hidden
zC(address@hidden(aU;+<6@|address@hidden(3!s`PqChr7ekSW5e<2RgHT
zG)~2*Bwbh=?w9#xC+;;<Bd2##=C+2aF5=rfZdcfM`kSNP=(u=7&iaG#VcFZhbg?Mz
address@hidden;address@hidden&D6psXZQ1;$-jSSU_dEP&AZ5Wk1<!I#W
zNWiD4uFShmwhu-sbmeeGC0n1eaa>address@hidden@3N;qHK_nB!ssmVN+zT!fz
address@hidden@address@hidden;}*>o%AWvlG#ZevwOo#kyqg_?`O9+YZphwbWx
zs<irc=A}L-H;NOrQ7>2JE7YvFRJWw)address@hidden;YmqVE8DK=<99|UBh)1
z<zet(9Hp~9bR|11w*qNYL=UIZQ>address@hidden
zdTWk!lXNBLTR-cK-%dYL<3AdW+;DYV%`KgEsl6TvJ9!s1>G57q{hBVYRgMmO%COEj
zP!jr^{k{I7o0<C|-!zG~YfIEOnpQZyAoK4JxBEM;address@hidden&ZxcI_-Jdl
z=v9qVR=cI{Flwu*9;$NaGEP*l*eP`(r)~d8{p17Jh{$re)mJ$V)StLti9?Pn$<@uM
za9vf6zhAxfRCU%%j-!FiPpX=3SXD3mR2|%;=IJ^e^(&KfjCWNfbdGxBR2dXi+z0vF
z<02}IzUq(KRnfQ&tL8mkOH}U<cZ<VED$~RAVEm2Z!j+4v>{Xc5Rc6%HjM^8jUR2dp
zJ5P0CHZnch?F-dL<M`oT`9QsXM7(iCW7h#)dVaQqb<address@hidden)>Q5I#-
zB^KSM`rDip$9OkxQKbt1$gNTL3GH0r)ZSIs%PI;hwkrZ0>SehV=%(address@hidden
zbR4&ge!^YNB-p4RmK=|#Dt=ugz90RtwDwCkrgG1{y^?m&i>address@hidden)FQo
zQgU;VDVdL)address@hidden>EeaE2+vp?wV&Fda1OAa!EP-s`_E0&Ii+ecd|84-KNDqYo1
address@hidden>B6Aq3JW6YB}m7BiRo~o#C65YM-6oE_`KHSxiLZMstQn<I}
zp+d;address@hidden@gM)address@hidden
zw)+qJ+ec1RG2D!fB;8~(o>7Y*6s78>@~QHVIHYz|T}Gc3hRQ=lM<r|Bou=x9tBWde
z^p+7`_Iv8-6%Duc%AWf-rBDPpL#Z3soQ$EiQ`O8>OZ76O+{>address@hidden)N
address@hidden;`tHV~<address@hidden(}4NZYavZ{4~Dhy53H;Z7a%3r+Tz^sr`jK_I;1Rv4*hg
address@hidden|1>@2)H^-CSDe_<)B3
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
z-b%GoTVPUx+%|RRNnhvh-5gE~e7z{^g|FG+eznN1T_Db^A2{kETdp<9)AFf*o3ffh
z16V(%Rwt;{*}cehngqIAR9JYQ2YV^SljO&vlPbB&N~lefnzuT<b}2na(O$7srjISS
zRF{aM{`=(ELX(wfKW3k4K)Ld(psKn%>C)address@hidden>Sbg6TRd4)o!
zZtwPA^DtDUd|##E6=}DlzJidej<U0JG}6no$6<C`i~9c)SsB0JV2~!8c|e+HuTig6
z-CTwGgG;e`K5a>Phf12_XlAQQw&j{MZGq{j<!pBx`IuIWXrf=gq&address@hidden
address@hidden;|address@hidden|3O8}(FVOp;#E?6g?z@;#dpN;p&
z<D+tXao&TIsuntz$uS;ey0Tx;`iU`}sgx>>{9;6+>2h`BX-|Do$XZ;A0Z&iOBz7$L
z+v*lUJ*9})si&aPmK`p<3hw((7KxwyT2%l=c%Q5VDtB2k^-A4l8jX+D+mE#kXx!gF
address@hidden&dJ^n(E>-W{E|S>C1BH_vp--oD`xiui<N|
address@hidden>%(`Zhm7~QNMFAk42O*nojwiL`X
z?wN5ybr*Gh?sqgukgJ1MZn8?P!<9Ni9q<+nCBpq${!Y`#7*!}ADT~qb#Ka<R`P2oR
zs)address@hidden)d=5!-ZW*i_VK948g3pMFM+05hYxO_F`P)*t`
zm5%-(O@@^X?dpw2S;jg<address@hidden)m9P)gNiAtaqn)x2{WNv!{pKRTr)Se4f$h
address@hidden<v6NPd-k?|g1U5K$?f216g^ngLaH>V
z+f>PD`%YB#_%5}2H}z^ejmt0BQ!VtXs_KzNY-f(;x_F=!-LIDQG^Y=%zRuCTe34kq
zPG|c0w{l(GeAlf=K4U-HnMvV6e#)0Zs5+v<@_`m7CUg6!nv+G26GfZ`{nc8F+M9YD
z?Nd^vNFhF+Ep1H#%0WC9(KuX9<!+Q?IrLWR$(Ee<18>z;vvv!dES*iJ;address@hidden
zlFsepNz#Uqs<ubhmFFZXG*!_UQF(si#;v_bZvHM*$Nx!n?P;(S6PfF70m3^vaI7<1
z+C=s8_E0me7!GIx=N$!lJ|address@hidden)|N^lG&$
zb%Qw)address@hidden>4^xkk6E=zMQGn3ZdE~?cK+2^J<o9gHt^B*O}T5)7MPH6}qi<e6pj9
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
zq^qwhbw&H+3&(v=JjR?>CN0M7c+vE|hQRUqn{EZMoRi+uKmVtGKfm6Y;address@hidden
zWRHkO%DbrtR-=#i4!HN!Xu0%Owb4+2dLUZXK~IB!b$orl87xm0XIe(I=d|Zo&p|yz
zS8d4eD$x!yE#Fi-!_|&address@hidden<OM_yq)USKqG9;)jhPY
zrdF?An?Lot5!{-K)E!LPaw`9sm+N7OdePV)(z&s=a%W?`d*}Y_xcPcac5-zn4v$sA
zzZdUaee^>5r3%q3+(*s!2h-T&N!6or&-ZcndLjrYYPd&address@hidden>!nltrun!
address@hidden;P}zWTlKBz;j<DY1HWuD_9X_Z*s}S39k>HapT&&Z-xx?ST|p
zR(5m`y-PJ#YrJJG-_MoAu~RD*Uf%E{YqeXyi&SA!Q=c>wRktvxPg;H_ldi3}5$#`F
zs`j-`TE4WUwJYt-b*n4~dZ4Mt?c<kTa!>Bis`Vp3osHfr*FVABS8{S@<NXbOuX~*0
z-+VDV*2^xa!8y_K{4^HD^Q*N}Nt-|7eZiyl*b{i|3(?8y5;Ydk6H;address@hidden
z8*t7?`B*kz%&address@hidden>o|JwSjnhw<4$aP(|18kTZ_zeSdhY#jJslm-Kr(
z=Hua>nqYM!y<BtANhnQC#3Pj41?QWqVtsF>nY`e~W3gjU&address@hidden(u$<M#M(MO9q
z+ANdz?CECTc4~^-y)EsHiQfN>w2{QWokF!_{a8(HIQXW1^N7Z3+AkQtheWr`KbY4T
zO<address@hidden|IeZu<LWb=AjOjKT~^*`62+46OMhhEUoZR35gmA2>n>WWUD8`ejg
zS}yhzG4(d_izOyYG=8EOyTjc<MYo?~m5Vy3L<address@hidden&address@hidden
zj1ObX5mn7YP+fLg&)D5oTu&S>PW`Tn_PyAxGP4jGzw073O<address@hidden<vTV
z`TkS={=d0DdUx)N{sn!%)oK;ah(2HTXFd=>009ILK%j8~j{pBG$N!%q{x`0C$q)hv
zAb<address@hidden|2)V4pC<m-5pfIw1Q0*~fyN0q{`>F$dj0=ri2sc%UowOM0tg_0K%Ic&
z|G&)f|7VH+bwnIP009ILK%j8~j{pBE$N%Sv|BWkOGK2sE2q1t!oq*&2zs~Xh=ZODx
zL>xl^0R#|0pm73@|Nkb(|address@hidden;8ApX}8aSQ<j5I_Kd
z#tAt7|8b80zexOVT=|kA1Q0*~0R-v<9RL4aj{m<z{I4V87y<|&fB*uG6L9?h_c{Lm
zGV#B0<x7STKmY**5U3My{QnO*{ujjmIwFoCfB*srAka7g$NzV8{Qna1zj5VDh7dpi
z0R#}J6L9?hk2(IIC;rzFaSQ<j5I_Kd#tAt7|EC=PzfAmZT=|kA1Q0*~0R-v<9RL4w
zj{m<x{I4V87y<|&fB*uG6L9?hFFF2yh4|address@hidden<address@hidden;|9_SEUq{3-
z1Q0*~0R$Q+;Q0UFa{PaR_}{qlB|``xfB*sr)CoBL|MwjKFA)Fhh&YA-0tg_0K;r})
z|Nlph{}+k>jVoU=ga85vAb>!ffaCxF%<=z4;(r|x#}GgO0R#|eoPgv1|H|?ItHl4t
zl`k1W009ILK%h>*@&AA4_&address@hidden&y{BK<Ok|6{TKmY**
z>I5AB|8I`}mx%v$L>xl^0R#|0pm73@|Nk$?|CfpXjVoU=ga85vAb>!ffaCw4<oJJ?
z_+LlFF$54m009IVC*b(address@hidden(8D)GOLh+_yKfB*sr
zG)}<rKSlt5^zPg%#Q(;XFBw7r0R#|0piaQi|8sf%|25)&9TCS6KmY**5NMo$<Ns4R
z{=Z86Z(RA3Ap{UW009K*1RVdL&hh^>;(r|x#}GgO0R#|eoPgv1*&P4BPW*3N`H~?7
z5I_I{1nLAF|DVb6|8?Sj9TCS6KmY**5NMo$<NvuF|F04M8&|$$2mu5TKmdU{0muK(
z=lK5(;(r|x#}GgO0R#|eoPgthJIDX)#Q(;XFBw7r0R#|0piaQ?|HpFte}nj6N5nA%
z5I_I{1R5vc`2U3*|9_46-?;K6LkJ*%00Icq2{``$c#i+yB>vYCaSQ<j5I_Kd#tAt7
z|G6Chf1UW>xbh`K2q1s}0tnOzIQ~DI<Nvpa|8+zhLjVB;address@hidden<@nzr{x`0C
z$q)hvAb<address@hidden<t5yucf009ILXq<rK|4-)lzd-zNT=|kA1Q0*~0R-v<
z9RL4(j{l3q|address@hidden)hvAb<address@hidden
z5yucf009ILXq<rK|4--me~b9vxbh`K2q1s}0tnOzIR5`kj{mob|8+zhLjVB;address@hidden
address@hidden&YA-0tg_0K;r})|9>vW|L+k0
z8&|$$2mu5TKmdU{0muKJ&address@hidden;zK`H~?75I_I{
z1nLAF|9>&Z|7GHT9TCS6KmY**5NMo$<Nq(address@hidden(RA3Ap{UW009K*1RVc=ImiEd
z#Q!=Xjv;address@hidden|address@hidden
z0*w=J{6C-L{~GbXapg;f5I_I{1Q4hbaQy#rj{ob#|2iU$A%Fk^2q4fn0muJe$?<=K
z_}{qlB|``xfB*sr)CoBLe<jENH;MmsL>xl^0R#|0pm73@|G%2!|69cW#+5G_LI42-
z5I~?#!14cu9RGiV_+LlFF$54m009IVC*b&hA;<sw#Q(;XFBw7r0R#|0piaQ?|6-2+
ze}VX4N5nA%5I_I{1R5vc`2S*#|9_GA-?;K6LkJ*%00Icq2{`_LHOK$IMEtKK;ur!5
zAb<address@hidden|00D(FI$N!gd{QoP&|2iU$A%Fk^2q4fn0muJK
zIsX4u;(z1Hmkc3*00IagP$%H{|8kE1-y{Cl5pfIw1Q0*~fyN0q{$I}VzeoIUT=|kA
z1Q0*~0R-v<address@hidden<address@hidden>0
zCCC45;(r|x#}GgO0R#|eoPgv1*K+(XiT{l&UowOM0tg_0K%Ic&|EoFv?-2j%h&YA-
z0tg_0K;r})|6j}T{{iv8apg;f5I_I{1Q4hbaQy#zj{m#F|2iU$A%Fk^2q4fn0muK>
zbNufU{~K4nWC#HS5I_KdIswQ3YdQXZpZH%##4!XAKmY**8YkfR|BW2~-zNSyu6)T5
z0tg_000MOaj{nzl{NE$~*Aa0H0R#|00D;B{IR3wp<NrSKzj5VDh7dpi0R#}J6L9?h
zwH*Hk#Q!=Xjv;^m0tg_`I0481Z|3+vB>p$9e8~_32q1s}0(An8|G%E&{{iv8j)-Fj
zAb<address@hidden>@&8*n{{JTNzj5VDh7dpi0R#}J6L9?R<oG`#{?`$43;_fXKmdWp2{``W
z$nk$n{BK<Ok|6{TKmY**>I5AB7jpbRB>vYCaSQ<j5I_Kd#tAt7FXs4vMEq}D`H~?7
z5I_I{1nLAF|GPQ<address@hidden;{}J)Oapg;f5I_I{1Q4hbaQwfO
z<NrIv|2iU$A%Fk^2q4fn0muK_IsP9L{~K4nWC#HS5I_KdIswQ3r5ykN8u7o5h+_yK
zfB*srG)}<r|JynK|8?Sj<I0x|A%Fk^2p~`=;address@hidden
address@hidden<address@hidden@=~<|8Eih>xejp00IagfI#B}{y!Oxwp0KB

diff --git a/test/testudf.c.in b/test/testudf.c.in
index 210bd12..3d22c6e 100644
--- a/test/testudf.c.in
+++ b/test/testudf.c.in
@@ -1,6 +1,6 @@
 /*
   Copyright (C) 2013 Rocky Bernstein <address@hidden>
-  Copyright (C) 2013 Pete Batard <address@hidden>
+  Copyright (C) 2013-2014 Pete Batard <address@hidden>
 
   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
@@ -44,18 +44,22 @@
 #include <cdio/cdio.h>
 #include <cdio/udf.h>
 
+#define EXPECTED_NAME    "FéжΘvrier"
+#define EXPECTED_LENGTH  10
+
 int
 main(int argc, const char *argv[])
 {
   char const *psz_fname = UDF_IMAGE;
   char volume_id[192];         /* 3*64 to account for UTF-8 */
   udf_t* p_udf = NULL;
-  udf_dirent_t* p_udf_root;
+  udf_dirent_t *p_udf_root, *p_udf_file;
+  int64_t file_length;
 
   p_udf = udf_open(psz_fname);
 
   if (NULL == p_udf) {
-    fprintf(stderr, "Sorry, couldn't open %s as an UDF image\n",
+    fprintf(stderr, "Couldn't open %s as an UDF image\n",
       psz_fname);
     return 1;
   }
@@ -66,14 +70,26 @@ main(int argc, const char *argv[])
     return 2;
   }
 
-  if (udf_get_logical_volume_id(p_udf, volume_id, sizeof(volume_id)) <= 0) {
-    fprintf(stderr, "UDF image %s has no logical volume ID\n",
-      psz_fname);
+  if ( (udf_get_logical_volume_id(p_udf, volume_id, sizeof(volume_id)) <= 0)
+    || (strcmp(EXPECTED_NAME, volume_id) != 0) ) {
+    fprintf(stderr, "Unexpected UDF logical volume ID\n");
     return 3;
   }
-  printf("-- Volume id: %s\n", volume_id);
+  printf("-- Good! Volume id matches expected UTF-8 data\n");
 
-  udf_close(p_udf);
+  p_udf_file = udf_fopen(p_udf_root, EXPECTED_NAME);
+  if (!p_udf_file) {
+    fprintf(stderr, "Could not locate expected file in UDF image\n");
+    return 4;
+  }
+  printf("-- Good! File name matches expected UTF-8 data\n");
+
+  file_length = udf_get_file_length(p_udf_file);
+  if (file_length != EXPECTED_LENGTH) {
+    fprintf(stderr, "Unexpected UDF file length\n");
+    return 5;
+  }
+  printf("-- Good! File length matches expected length\n");
 
   return 0;
 }
-- 
1.8.5.2.msysgit.0


reply via email to

[Prev in Thread] Current Thread [Next in Thread]