1 /****************************************************************************
   2  *
   3  * ftrfork.c
   4  *
   5  *   Embedded resource forks accessor (body).
   6  *
   7  * Copyright (C) 2004-2020 by
   8  * Masatake YAMATO and Redhat K.K.
   9  *
  10  * FT_Raccess_Get_HeaderInfo() and raccess_guess_darwin_hfsplus() are
  11  * derived from ftobjs.c.
  12  *
  13  * This file is part of the FreeType project, and may only be used,
  14  * modified, and distributed under the terms of the FreeType project
  15  * license, LICENSE.TXT.  By continuing to use, modify, or distribute
  16  * this file you indicate that you have read the license and
  17  * understand and accept it fully.
  18  *
  19  */
  20 
  21 /****************************************************************************
  22  * Development of the code in this file is support of
  23  * Information-technology Promotion Agency, Japan.
  24  */
  25 
  26 
  27 #include <ft2build.h>
  28 #include FT_INTERNAL_DEBUG_H
  29 #include FT_INTERNAL_STREAM_H
  30 #include FT_INTERNAL_RFORK_H
  31 
  32 #include "ftbase.h"
  33 
  34 #undef  FT_COMPONENT
  35 #define FT_COMPONENT  raccess
  36 
  37 
  38   /*************************************************************************/
  39   /*************************************************************************/
  40   /*************************************************************************/
  41   /****                                                                 ****/
  42   /****                                                                 ****/
  43   /****               Resource fork directory access                    ****/
  44   /****                                                                 ****/
  45   /****                                                                 ****/
  46   /*************************************************************************/
  47   /*************************************************************************/
  48   /*************************************************************************/
  49 
  50   FT_BASE_DEF( FT_Error )
  51   FT_Raccess_Get_HeaderInfo( FT_Library  library,
  52                              FT_Stream   stream,
  53                              FT_Long     rfork_offset,
  54                              FT_Long    *map_offset,
  55                              FT_Long    *rdata_pos )
  56   {
  57     FT_Error       error;
  58     unsigned char  head[16], head2[16];
  59     FT_Long        map_pos, map_len, rdata_len;
  60     int            allzeros, allmatch, i;
  61     FT_Long        type_list;
  62 
  63     FT_UNUSED( library );
  64 
  65 
  66     error = FT_Stream_Seek( stream, (FT_ULong)rfork_offset );
  67     if ( error )
  68       return error;
  69 
  70     error = FT_Stream_Read( stream, (FT_Byte*)head, 16 );
  71     if ( error )
  72       return error;
  73 
  74     /* ensure positive values */
  75     if ( head[0]  >= 0x80 ||
  76          head[4]  >= 0x80 ||
  77          head[8]  >= 0x80 ||
  78          head[12] >= 0x80 )
  79       return FT_THROW( Unknown_File_Format );
  80 
  81     *rdata_pos = ( head[ 0] << 24 ) |
  82                  ( head[ 1] << 16 ) |
  83                  ( head[ 2] <<  8 ) |
  84                    head[ 3];
  85     map_pos    = ( head[ 4] << 24 ) |
  86                  ( head[ 5] << 16 ) |
  87                  ( head[ 6] <<  8 ) |
  88                    head[ 7];
  89     rdata_len  = ( head[ 8] << 24 ) |
  90                  ( head[ 9] << 16 ) |
  91                  ( head[10] <<  8 ) |
  92                    head[11];
  93     map_len    = ( head[12] << 24 ) |
  94                  ( head[13] << 16 ) |
  95                  ( head[14] <<  8 ) |
  96                    head[15];
  97 
  98     /* the map must not be empty */
  99     if ( !map_pos )
 100       return FT_THROW( Unknown_File_Format );
 101 
 102     /* check whether rdata and map overlap */
 103     if ( *rdata_pos < map_pos )
 104     {
 105       if ( *rdata_pos > map_pos - rdata_len )
 106         return FT_THROW( Unknown_File_Format );
 107     }
 108     else
 109     {
 110       if ( map_pos > *rdata_pos - map_len )
 111         return FT_THROW( Unknown_File_Format );
 112     }
 113 
 114     /* check whether end of rdata or map exceeds stream size */
 115     if ( FT_LONG_MAX - rdata_len < *rdata_pos                               ||
 116          FT_LONG_MAX - map_len < map_pos                                    ||
 117 
 118          FT_LONG_MAX - ( *rdata_pos + rdata_len ) < rfork_offset            ||
 119          FT_LONG_MAX - ( map_pos + map_len ) < rfork_offset                 ||
 120 
 121          (FT_ULong)( rfork_offset + *rdata_pos + rdata_len ) > stream->size ||
 122          (FT_ULong)( rfork_offset + map_pos + map_len ) > stream->size      )
 123       return FT_THROW( Unknown_File_Format );
 124 
 125     *rdata_pos += rfork_offset;
 126     map_pos    += rfork_offset;
 127 
 128     error = FT_Stream_Seek( stream, (FT_ULong)map_pos );
 129     if ( error )
 130       return error;
 131 
 132     head2[15] = (FT_Byte)( head[15] + 1 );       /* make it be different */
 133 
 134     error = FT_Stream_Read( stream, (FT_Byte*)head2, 16 );
 135     if ( error )
 136       return error;
 137 
 138     allzeros = 1;
 139     allmatch = 1;
 140     for ( i = 0; i < 16; i++ )
 141     {
 142       if ( head2[i] != 0 )
 143         allzeros = 0;
 144       if ( head2[i] != head[i] )
 145         allmatch = 0;
 146     }
 147     if ( !allzeros && !allmatch )
 148       return FT_THROW( Unknown_File_Format );
 149 
 150     /* If we have reached this point then it is probably a mac resource */
 151     /* file.  Now, does it contain any interesting resources?           */
 152 
 153     (void)FT_STREAM_SKIP( 4        /* skip handle to next resource map */
 154                           + 2      /* skip file resource number */
 155                           + 2 );   /* skip attributes */
 156 
 157     if ( FT_READ_SHORT( type_list ) )
 158       return error;
 159     if ( type_list < 0 )
 160       return FT_THROW( Unknown_File_Format );
 161 
 162     error = FT_Stream_Seek( stream, (FT_ULong)( map_pos + type_list ) );
 163     if ( error )
 164       return error;
 165 
 166     *map_offset = map_pos + type_list;
 167     return FT_Err_Ok;
 168   }
 169 
 170 
 171   static int
 172   ft_raccess_sort_ref_by_id( FT_RFork_Ref*  a,
 173                              FT_RFork_Ref*  b )
 174   {
 175     if ( a->res_id < b->res_id )
 176       return -1;
 177     else if ( a->res_id > b->res_id )
 178       return 1;
 179     else
 180       return 0;
 181   }
 182 
 183 
 184   FT_BASE_DEF( FT_Error )
 185   FT_Raccess_Get_DataOffsets( FT_Library  library,
 186                               FT_Stream   stream,
 187                               FT_Long     map_offset,
 188                               FT_Long     rdata_pos,
 189                               FT_Long     tag,
 190                               FT_Bool     sort_by_res_id,
 191                               FT_Long   **offsets,
 192                               FT_Long    *count )
 193   {
 194     FT_Error      error;
 195     int           i, j, cnt, subcnt;
 196     FT_Long       tag_internal, rpos;
 197     FT_Memory     memory = library->memory;
 198     FT_Long       temp;
 199     FT_Long       *offsets_internal = NULL;
 200     FT_RFork_Ref  *ref = NULL;
 201 
 202 
 203     FT_TRACE3(( "\n" ));
 204     error = FT_Stream_Seek( stream, (FT_ULong)map_offset );
 205     if ( error )
 206       return error;
 207 
 208     if ( FT_READ_SHORT( cnt ) )
 209       return error;
 210     cnt++;
 211 
 212     /* `rpos' is a signed 16bit integer offset to resource records; the    */
 213     /* size of a resource record is 12 bytes.  The map header is 28 bytes, */
 214     /* and a type list needs 10 bytes or more.  If we assume that the name */
 215     /* list is empty and we have only a single entry in the type list,     */
 216     /* there can be at most                                                */
 217     /*                                                                     */
 218     /*   (32768 - 28 - 10) / 12 = 2727                                     */
 219     /*                                                                     */
 220     /* resources.                                                          */
 221     /*                                                                     */
 222     /* A type list starts with a two-byte counter, followed by 10-byte     */
 223     /* type records.  Assuming that there are no resources, the number of  */
 224     /* type records can be at most                                         */
 225     /*                                                                     */
 226     /*   (32768 - 28 - 2) / 8 = 4079                                       */
 227     /*                                                                     */
 228     if ( cnt > 4079 )
 229       return FT_THROW( Invalid_Table );
 230 
 231     for ( i = 0; i < cnt; i++ )
 232     {
 233       if ( FT_READ_LONG( tag_internal ) ||
 234            FT_READ_SHORT( subcnt )      ||
 235            FT_READ_SHORT( rpos )        )
 236         return error;
 237 
 238       FT_TRACE2(( "Resource tags: %c%c%c%c\n",
 239                   (char)( 0xFF & ( tag_internal >> 24 ) ),
 240                   (char)( 0xFF & ( tag_internal >> 16 ) ),
 241                   (char)( 0xFF & ( tag_internal >>  8 ) ),
 242                   (char)( 0xFF & ( tag_internal >>  0 ) ) ));
 243       FT_TRACE3(( "             : subcount=%d, suboffset=0x%04x\n",
 244                   subcnt, rpos ));
 245 
 246       if ( tag_internal == tag )
 247       {
 248         *count = subcnt + 1;
 249         rpos  += map_offset;
 250 
 251         /* a zero count might be valid in the resource specification, */
 252         /* however, it is completely useless to us                    */
 253         if ( *count < 1 || *count > 2727 )
 254           return FT_THROW( Invalid_Table );
 255 
 256         error = FT_Stream_Seek( stream, (FT_ULong)rpos );
 257         if ( error )
 258           return error;
 259 
 260         if ( FT_NEW_ARRAY( ref, *count ) )
 261           return error;
 262 
 263         for ( j = 0; j < *count; j++ )
 264         {
 265           if ( FT_READ_SHORT( ref[j].res_id ) )
 266             goto Exit;
 267           if ( FT_STREAM_SKIP( 2 ) )  /* resource name offset */
 268             goto Exit;
 269           if ( FT_READ_LONG( temp ) ) /* attributes (8bit), offset (24bit) */
 270             goto Exit;
 271           if ( FT_STREAM_SKIP( 4 ) )  /* mbz */
 272             goto Exit;
 273 
 274           /*
 275            * According to Inside Macintosh: More Macintosh Toolbox,
 276            * "Resource IDs" (1-46), there are some reserved IDs.
 277            * However, FreeType2 is not a font synthesizer, no need
 278            * to check the acceptable resource ID.
 279            */
 280           if ( temp < 0 )
 281           {
 282             error = FT_THROW( Invalid_Table );
 283             goto Exit;
 284           }
 285 
 286           ref[j].offset = temp & 0xFFFFFFL;
 287 
 288           FT_TRACE3(( "             [%d]:"
 289                       " resource_id=0x%04x, offset=0x%08x\n",
 290                       j, (FT_UShort)ref[j].res_id, ref[j].offset ));
 291         }
 292 
 293         if ( sort_by_res_id )
 294         {
 295           ft_qsort( ref,
 296                     (size_t)*count,
 297                     sizeof ( FT_RFork_Ref ),
 298                     ( int(*)(const void*,
 299                              const void*) )ft_raccess_sort_ref_by_id );
 300 
 301           FT_TRACE3(( "             -- sort resources by their ids --\n" ));
 302 
 303           for ( j = 0; j < *count; j++ )
 304             FT_TRACE3(( "             [%d]:"
 305                         " resource_id=0x%04x, offset=0x%08x\n",
 306                         j, ref[j].res_id, ref[j].offset ));
 307         }
 308 
 309         if ( FT_NEW_ARRAY( offsets_internal, *count ) )
 310           goto Exit;
 311 
 312         /* XXX: duplicated reference ID,
 313          *      gap between reference IDs are acceptable?
 314          *      further investigation on Apple implementation is needed.
 315          */
 316         for ( j = 0; j < *count; j++ )
 317           offsets_internal[j] = rdata_pos + ref[j].offset;
 318 
 319         *offsets = offsets_internal;
 320         error    = FT_Err_Ok;
 321 
 322       Exit:
 323         FT_FREE( ref );
 324         return error;
 325       }
 326     }
 327 
 328     return FT_THROW( Cannot_Open_Resource );
 329   }
 330 
 331 
 332 #ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK
 333 
 334   /*************************************************************************/
 335   /*************************************************************************/
 336   /*************************************************************************/
 337   /****                                                                 ****/
 338   /****                                                                 ****/
 339   /****                     Guessing functions                          ****/
 340   /****                                                                 ****/
 341   /****            When you add a new guessing function,                ****/
 342   /****           update FT_RACCESS_N_RULES in ftrfork.h.               ****/
 343   /****                                                                 ****/
 344   /*************************************************************************/
 345   /*************************************************************************/
 346   /*************************************************************************/
 347 
 348   static FT_Error
 349   raccess_guess_apple_double( FT_Library  library,
 350                               FT_Stream   stream,
 351                               char       *base_file_name,
 352                               char      **result_file_name,
 353                               FT_Long    *result_offset );
 354 
 355   static FT_Error
 356   raccess_guess_apple_single( FT_Library  library,
 357                               FT_Stream   stream,
 358                               char       *base_file_name,
 359                               char      **result_file_name,
 360                               FT_Long    *result_offset );
 361 
 362   static FT_Error
 363   raccess_guess_darwin_ufs_export( FT_Library  library,
 364                                    FT_Stream   stream,
 365                                    char       *base_file_name,
 366                                    char      **result_file_name,
 367                                    FT_Long    *result_offset );
 368 
 369   static FT_Error
 370   raccess_guess_darwin_newvfs( FT_Library  library,
 371                                FT_Stream   stream,
 372                                char       *base_file_name,
 373                                char      **result_file_name,
 374                                FT_Long    *result_offset );
 375 
 376   static FT_Error
 377   raccess_guess_darwin_hfsplus( FT_Library  library,
 378                                 FT_Stream   stream,
 379                                 char       *base_file_name,
 380                                 char      **result_file_name,
 381                                 FT_Long    *result_offset );
 382 
 383   static FT_Error
 384   raccess_guess_vfat( FT_Library  library,
 385                       FT_Stream   stream,
 386                       char       *base_file_name,
 387                       char      **result_file_name,
 388                       FT_Long    *result_offset );
 389 
 390   static FT_Error
 391   raccess_guess_linux_cap( FT_Library  library,
 392                            FT_Stream   stream,
 393                            char       *base_file_name,
 394                            char      **result_file_name,
 395                            FT_Long    *result_offset );
 396 
 397   static FT_Error
 398   raccess_guess_linux_double( FT_Library  library,
 399                               FT_Stream   stream,
 400                               char       *base_file_name,
 401                               char      **result_file_name,
 402                               FT_Long    *result_offset );
 403 
 404   static FT_Error
 405   raccess_guess_linux_netatalk( FT_Library  library,
 406                                 FT_Stream   stream,
 407                                 char       *base_file_name,
 408                                 char      **result_file_name,
 409                                 FT_Long    *result_offset );
 410 
 411 
 412   CONST_FT_RFORK_RULE_ARRAY_BEGIN(ft_raccess_guess_table,
 413                                   ft_raccess_guess_rec)
 414   CONST_FT_RFORK_RULE_ARRAY_ENTRY(apple_double,      apple_double)
 415   CONST_FT_RFORK_RULE_ARRAY_ENTRY(apple_single,      apple_single)
 416   CONST_FT_RFORK_RULE_ARRAY_ENTRY(darwin_ufs_export, darwin_ufs_export)
 417   CONST_FT_RFORK_RULE_ARRAY_ENTRY(darwin_newvfs,     darwin_newvfs)
 418   CONST_FT_RFORK_RULE_ARRAY_ENTRY(darwin_hfsplus,    darwin_hfsplus)
 419   CONST_FT_RFORK_RULE_ARRAY_ENTRY(vfat,              vfat)
 420   CONST_FT_RFORK_RULE_ARRAY_ENTRY(linux_cap,         linux_cap)
 421   CONST_FT_RFORK_RULE_ARRAY_ENTRY(linux_double,      linux_double)
 422   CONST_FT_RFORK_RULE_ARRAY_ENTRY(linux_netatalk,    linux_netatalk)
 423   CONST_FT_RFORK_RULE_ARRAY_END
 424 
 425 
 426   /*************************************************************************/
 427   /****                                                                 ****/
 428   /****                       Helper functions                          ****/
 429   /****                                                                 ****/
 430   /*************************************************************************/
 431 
 432   static FT_Error
 433   raccess_guess_apple_generic( FT_Library  library,
 434                                FT_Stream   stream,
 435                                char       *base_file_name,
 436                                FT_Int32    magic,
 437                                FT_Long    *result_offset );
 438 
 439   static FT_Error
 440   raccess_guess_linux_double_from_file_name( FT_Library  library,
 441                                              char*       file_name,
 442                                              FT_Long    *result_offset );
 443 
 444   static char *
 445   raccess_make_file_name( FT_Memory    memory,
 446                           const char  *original_name,
 447                           const char  *insertion );
 448 
 449   FT_BASE_DEF( void )
 450   FT_Raccess_Guess( FT_Library  library,
 451                     FT_Stream   stream,
 452                     char*       base_name,
 453                     char      **new_names,
 454                     FT_Long    *offsets,
 455                     FT_Error   *errors )
 456   {
 457     FT_Int  i;
 458 
 459 
 460     for ( i = 0; i < FT_RACCESS_N_RULES; i++ )
 461     {
 462       new_names[i] = NULL;
 463       if ( NULL != stream )
 464         errors[i] = FT_Stream_Seek( stream, 0 );
 465       else
 466         errors[i] = FT_Err_Ok;
 467 
 468       if ( errors[i] )
 469         continue;
 470 
 471       errors[i] = ft_raccess_guess_table[i].func( library,
 472                                                   stream, base_name,
 473                                                   &(new_names[i]),
 474                                                   &(offsets[i]) );
 475     }
 476 
 477     return;
 478   }
 479 
 480 
 481 #if defined( FT_CONFIG_OPTION_MAC_FONTS ) && !defined( FT_MACINTOSH )
 482   static FT_RFork_Rule
 483   raccess_get_rule_type_from_rule_index( FT_Library  library,
 484                                          FT_UInt     rule_index )
 485   {
 486     FT_UNUSED( library );
 487 
 488     if ( rule_index >= FT_RACCESS_N_RULES )
 489       return FT_RFork_Rule_invalid;
 490 
 491     return ft_raccess_guess_table[rule_index].type;
 492   }
 493 
 494 
 495   /*
 496    * For this function, refer ftbase.h.
 497    */
 498   FT_LOCAL_DEF( FT_Bool )
 499   ft_raccess_rule_by_darwin_vfs( FT_Library  library,
 500                                  FT_UInt     rule_index )
 501   {
 502     switch( raccess_get_rule_type_from_rule_index( library, rule_index ) )
 503     {
 504       case FT_RFork_Rule_darwin_newvfs:
 505       case FT_RFork_Rule_darwin_hfsplus:
 506         return TRUE;
 507 
 508       default:
 509         return FALSE;
 510     }
 511   }
 512 #endif
 513 
 514 
 515   static FT_Error
 516   raccess_guess_apple_double( FT_Library  library,
 517                               FT_Stream   stream,
 518                               char       *base_file_name,
 519                               char      **result_file_name,
 520                               FT_Long    *result_offset )
 521   {
 522     FT_Int32  magic = ( 0x00 << 24 ) |
 523                       ( 0x05 << 16 ) |
 524                       ( 0x16 <<  8 ) |
 525                         0x07;
 526 
 527 
 528     *result_file_name = NULL;
 529     if ( NULL == stream )
 530       return FT_THROW( Cannot_Open_Stream );
 531 
 532     return raccess_guess_apple_generic( library, stream, base_file_name,
 533                                         magic, result_offset );
 534   }
 535 
 536 
 537   static FT_Error
 538   raccess_guess_apple_single( FT_Library  library,
 539                               FT_Stream   stream,
 540                               char       *base_file_name,
 541                               char      **result_file_name,
 542                               FT_Long    *result_offset )
 543   {
 544     FT_Int32  magic = ( 0x00 << 24 ) |
 545                       ( 0x05 << 16 ) |
 546                       ( 0x16 <<  8 ) |
 547                         0x00;
 548 
 549 
 550     *result_file_name = NULL;
 551     if ( NULL == stream )
 552       return FT_THROW( Cannot_Open_Stream );
 553 
 554     return raccess_guess_apple_generic( library, stream, base_file_name,
 555                                         magic, result_offset );
 556   }
 557 
 558 
 559   static FT_Error
 560   raccess_guess_darwin_ufs_export( FT_Library  library,
 561                                    FT_Stream   stream,
 562                                    char       *base_file_name,
 563                                    char      **result_file_name,
 564                                    FT_Long    *result_offset )
 565   {
 566     char*      newpath;
 567     FT_Error   error;
 568     FT_Memory  memory;
 569 
 570     FT_UNUSED( stream );
 571 
 572 
 573     memory  = library->memory;
 574     newpath = raccess_make_file_name( memory, base_file_name, "._" );
 575     if ( !newpath )
 576       return FT_THROW( Out_Of_Memory );
 577 
 578     error = raccess_guess_linux_double_from_file_name( library, newpath,
 579                                                        result_offset );
 580     if ( !error )
 581       *result_file_name = newpath;
 582     else
 583       FT_FREE( newpath );
 584 
 585     return error;
 586   }
 587 
 588 
 589   static FT_Error
 590   raccess_guess_darwin_hfsplus( FT_Library  library,
 591                                 FT_Stream   stream,
 592                                 char       *base_file_name,
 593                                 char      **result_file_name,
 594                                 FT_Long    *result_offset )
 595   {
 596     /*
 597       Only meaningful on systems with hfs+ drivers (or Macs).
 598      */
 599     FT_Error   error;
 600     char*      newpath = NULL;
 601     FT_Memory  memory;
 602     FT_Long    base_file_len = (FT_Long)ft_strlen( base_file_name );
 603 
 604     FT_UNUSED( stream );
 605 
 606 
 607     memory = library->memory;
 608 
 609     if ( base_file_len + 6 > FT_INT_MAX )
 610       return FT_THROW( Array_Too_Large );
 611 
 612     if ( FT_ALLOC( newpath, base_file_len + 6 ) )
 613       return error;
 614 
 615     FT_MEM_COPY( newpath, base_file_name, base_file_len );
 616     FT_MEM_COPY( newpath + base_file_len, "/rsrc", 6 );
 617 
 618     *result_file_name = newpath;
 619     *result_offset    = 0;
 620 
 621     return FT_Err_Ok;
 622   }
 623 
 624 
 625   static FT_Error
 626   raccess_guess_darwin_newvfs( FT_Library  library,
 627                                FT_Stream   stream,
 628                                char       *base_file_name,
 629                                char      **result_file_name,
 630                                FT_Long    *result_offset )
 631   {
 632     /*
 633       Only meaningful on systems with Mac OS X (> 10.1).
 634      */
 635     FT_Error   error;
 636     char*      newpath = NULL;
 637     FT_Memory  memory;
 638     FT_Long    base_file_len = (FT_Long)ft_strlen( base_file_name );
 639 
 640     FT_UNUSED( stream );
 641 
 642 
 643     memory = library->memory;
 644 
 645     if ( base_file_len + 18 > FT_INT_MAX )
 646       return FT_THROW( Array_Too_Large );
 647 
 648     if ( FT_ALLOC( newpath, base_file_len + 18 ) )
 649       return error;
 650 
 651     FT_MEM_COPY( newpath, base_file_name, base_file_len );
 652     FT_MEM_COPY( newpath + base_file_len, "/..namedfork/rsrc", 18 );
 653 
 654     *result_file_name = newpath;
 655     *result_offset    = 0;
 656 
 657     return FT_Err_Ok;
 658   }
 659 
 660 
 661   static FT_Error
 662   raccess_guess_vfat( FT_Library  library,
 663                       FT_Stream   stream,
 664                       char       *base_file_name,
 665                       char      **result_file_name,
 666                       FT_Long    *result_offset )
 667   {
 668     char*      newpath;
 669     FT_Memory  memory;
 670 
 671     FT_UNUSED( stream );
 672 
 673 
 674     memory = library->memory;
 675 
 676     newpath = raccess_make_file_name( memory, base_file_name,
 677                                       "resource.frk/" );
 678     if ( !newpath )
 679       return FT_THROW( Out_Of_Memory );
 680 
 681     *result_file_name = newpath;
 682     *result_offset    = 0;
 683 
 684     return FT_Err_Ok;
 685   }
 686 
 687 
 688   static FT_Error
 689   raccess_guess_linux_cap( FT_Library  library,
 690                            FT_Stream   stream,
 691                            char       *base_file_name,
 692                            char      **result_file_name,
 693                            FT_Long    *result_offset )
 694   {
 695     char*      newpath;
 696     FT_Memory  memory;
 697 
 698     FT_UNUSED( stream );
 699 
 700 
 701     memory = library->memory;
 702 
 703     newpath = raccess_make_file_name( memory, base_file_name, ".resource/" );
 704     if ( !newpath )
 705       return FT_THROW( Out_Of_Memory );
 706 
 707     *result_file_name = newpath;
 708     *result_offset    = 0;
 709 
 710     return FT_Err_Ok;
 711   }
 712 
 713 
 714   static FT_Error
 715   raccess_guess_linux_double( FT_Library  library,
 716                               FT_Stream   stream,
 717                               char       *base_file_name,
 718                               char      **result_file_name,
 719                               FT_Long    *result_offset )
 720   {
 721     char*      newpath;
 722     FT_Error   error;
 723     FT_Memory  memory;
 724 
 725     FT_UNUSED( stream );
 726 
 727 
 728     memory = library->memory;
 729 
 730     newpath = raccess_make_file_name( memory, base_file_name, "%" );
 731     if ( !newpath )
 732       return FT_THROW( Out_Of_Memory );
 733 
 734     error = raccess_guess_linux_double_from_file_name( library, newpath,
 735                                                        result_offset );
 736     if ( !error )
 737       *result_file_name = newpath;
 738     else
 739       FT_FREE( newpath );
 740 
 741     return error;
 742   }
 743 
 744 
 745   static FT_Error
 746   raccess_guess_linux_netatalk( FT_Library  library,
 747                                 FT_Stream   stream,
 748                                 char       *base_file_name,
 749                                 char      **result_file_name,
 750                                 FT_Long    *result_offset )
 751   {
 752     char*      newpath;
 753     FT_Error   error;
 754     FT_Memory  memory;
 755 
 756     FT_UNUSED( stream );
 757 
 758 
 759     memory = library->memory;
 760 
 761     newpath = raccess_make_file_name( memory, base_file_name,
 762                                       ".AppleDouble/" );
 763     if ( !newpath )
 764       return FT_THROW( Out_Of_Memory );
 765 
 766     error = raccess_guess_linux_double_from_file_name( library, newpath,
 767                                                        result_offset );
 768     if ( !error )
 769       *result_file_name = newpath;
 770     else
 771       FT_FREE( newpath );
 772 
 773     return error;
 774   }
 775 
 776 
 777   static FT_Error
 778   raccess_guess_apple_generic( FT_Library  library,
 779                                FT_Stream   stream,
 780                                char       *base_file_name,
 781                                FT_Int32    magic,
 782                                FT_Long    *result_offset )
 783   {
 784     FT_Int32   magic_from_stream;
 785     FT_Error   error;
 786     FT_Int32   version_number = 0;
 787     FT_UShort  n_of_entries;
 788 
 789     int        i;
 790     FT_Int32   entry_id, entry_offset, entry_length = 0;
 791 
 792     const FT_Int32  resource_fork_entry_id = 0x2;
 793 
 794     FT_UNUSED( library );
 795     FT_UNUSED( base_file_name );
 796     FT_UNUSED( version_number );
 797     FT_UNUSED( entry_length   );
 798 
 799 
 800     if ( FT_READ_LONG( magic_from_stream ) )
 801       return error;
 802     if ( magic_from_stream != magic )
 803       return FT_THROW( Unknown_File_Format );
 804 
 805     if ( FT_READ_LONG( version_number ) )
 806       return error;
 807 
 808     /* filler */
 809     error = FT_Stream_Skip( stream, 16 );
 810     if ( error )
 811       return error;
 812 
 813     if ( FT_READ_USHORT( n_of_entries ) )
 814       return error;
 815     if ( n_of_entries == 0 )
 816       return FT_THROW( Unknown_File_Format );
 817 
 818     for ( i = 0; i < n_of_entries; i++ )
 819     {
 820       if ( FT_READ_LONG( entry_id ) )
 821         return error;
 822       if ( entry_id == resource_fork_entry_id )
 823       {
 824         if ( FT_READ_LONG( entry_offset ) ||
 825              FT_READ_LONG( entry_length ) )
 826           continue;
 827         *result_offset = entry_offset;
 828 
 829         return FT_Err_Ok;
 830       }
 831       else
 832       {
 833         error = FT_Stream_Skip( stream, 4 + 4 );    /* offset + length */
 834         if ( error )
 835           return error;
 836       }
 837     }
 838 
 839     return FT_THROW( Unknown_File_Format );
 840   }
 841 
 842 
 843   static FT_Error
 844   raccess_guess_linux_double_from_file_name( FT_Library  library,
 845                                              char       *file_name,
 846                                              FT_Long    *result_offset )
 847   {
 848     FT_Open_Args  args2;
 849     FT_Stream     stream2;
 850     char*         nouse = NULL;
 851     FT_Error      error;
 852 
 853 
 854     args2.flags    = FT_OPEN_PATHNAME;
 855     args2.pathname = file_name;
 856     error = FT_Stream_New( library, &args2, &stream2 );
 857     if ( error )
 858       return error;
 859 
 860     error = raccess_guess_apple_double( library, stream2, file_name,
 861                                         &nouse, result_offset );
 862 
 863     FT_Stream_Free( stream2, 0 );
 864 
 865     return error;
 866   }
 867 
 868 
 869   static char*
 870   raccess_make_file_name( FT_Memory    memory,
 871                           const char  *original_name,
 872                           const char  *insertion )
 873   {
 874     char*        new_name = NULL;
 875     const char*  tmp;
 876     const char*  slash;
 877     size_t       new_length;
 878     FT_Error     error = FT_Err_Ok;
 879 
 880     FT_UNUSED( error );
 881 
 882 
 883     new_length = ft_strlen( original_name ) + ft_strlen( insertion );
 884     if ( FT_ALLOC( new_name, new_length + 1 ) )
 885       return NULL;
 886 
 887     tmp = ft_strrchr( original_name, '/' );
 888     if ( tmp )
 889     {
 890       ft_strncpy( new_name,
 891                   original_name,
 892                   (size_t)( tmp - original_name + 1 ) );
 893       new_name[tmp - original_name + 1] = '\0';
 894       slash = tmp + 1;
 895     }
 896     else
 897     {
 898       slash       = original_name;
 899       new_name[0] = '\0';
 900     }
 901 
 902     ft_strcat( new_name, insertion );
 903     ft_strcat( new_name, slash );
 904 
 905     return new_name;
 906   }
 907 
 908 
 909 #else   /* !FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */
 910 
 911 
 912   /**************************************************************************
 913    *                 Dummy function; just sets errors
 914    */
 915 
 916   FT_BASE_DEF( void )
 917   FT_Raccess_Guess( FT_Library  library,
 918                     FT_Stream   stream,
 919                     char       *base_name,
 920                     char      **new_names,
 921                     FT_Long    *offsets,
 922                     FT_Error   *errors )
 923   {
 924     FT_Int  i;
 925 
 926     FT_UNUSED( library );
 927     FT_UNUSED( stream );
 928     FT_UNUSED( base_name );
 929 
 930 
 931     for ( i = 0; i < FT_RACCESS_N_RULES; i++ )
 932     {
 933       new_names[i] = NULL;
 934       offsets[i]   = 0;
 935       errors[i]    = FT_ERR( Unimplemented_Feature );
 936     }
 937   }
 938 
 939 
 940 #endif  /* !FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */
 941 
 942 
 943 /* END */