Logo Search packages:      
Sourcecode: cdrkit version File versions  Download package

apple.c

/*
 * 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.
 *
 */

/* @(#)apple.c    1.19 04/03/02 joerg, Copyright 1997, 1998, 1999, 2000 James Pearson */
/*
 *      Copyright (c) 1997, 1998, 1999, 2000 James Pearson
 *
 * 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 *    Unix-HFS file interface including maping file extensions to TYPE/CREATOR
 *
 *    Adapted from mkhfs routines for mkhybrid
 *
 *    James Pearson 1/5/97
 *    Bug fix JCP 4/12/97
 *    Updated for 1.12 and added more Unix HFS filetypes. JCP 21/1/98
 *    Tidy up to use Finfo and Dinfo for all formats where
 *          possible JCP 25/4/2000
 *
 *    Things still to de done:
 *
 *          Check file size = finder + rsrc [+ data] is needed
 */

#ifdef APPLE_HYB

#include <mconfig.h>
#include "genisoimage.h"
#include <errno.h>
#include <fctldefs.h>
#include <utypes.h>
#include <ctype.h>
#include <netinet/in.h>
#include "apple.h"
#include <schily.h>

#ifdef USE_MAGIC
#include <magic.h>
magic_t magic_state = NULL;
#ifndef MAGIC_ERROR
/* workaround for older ┬┤API */
#define MAGIC_ERROR 0
#endif
#endif /* USE_MAGIC */

/* tidy up genisoimage definition ... */
typedef struct directory_entry dir_ent;

/* routines for getting HFS names and info */
#ifndef     HAVE_STRCASECMP
static int  strcasecmp(const char *s1, const char *s2);
#endif
static int  get_none_dir(char *, char *, dir_ent *, int);
static int  get_none_info(char *, char *, dir_ent *, int);
static int  get_cap_dir(char *, char *, dir_ent *, int);
static int  get_cap_info(char *, char *, dir_ent *, int);
static int  get_es_dir(char *, char *, dir_ent *, int);
static int  get_es_info(char *, char *, dir_ent *, int);
static int  get_dbl_dir(char *, char *, dir_ent *, int);
static int  get_dbl_info(char *, char *, dir_ent *, int);
static int  get_mb_info(char *, char *, dir_ent *, int);
static int  get_sgl_info(char *, char *, dir_ent *, int);
static int  get_fe_dir(char *, char *, dir_ent *, int);
static int  get_fe_info(char *, char *, dir_ent *, int);
static int  get_sgi_dir(char *, char *, dir_ent *, int);
static int  get_sgi_info(char *, char *, dir_ent *, int);
static int  get_sfm_info(char *, char *, dir_ent *, int);

#ifdef IS_MACOS_X
static int  get_xhfs_dir(char *, char *, dir_ent *, int);
static int  get_xhfs_info(char *, char *, dir_ent *, int);
#else
#define     get_xhfs_dir      get_none_dir
#define     get_xhfs_info     get_none_info
#endif /* IS_MACOS_X */

static void set_ct(hfsdirent *, char *, char *);
static void set_Dinfo(byte *, hfsdirent *);
static void set_Finfo(byte *, hfsdirent *);
static void cstrncpy(char *, char *, int);
static unsigned char dehex(char);
static unsigned char hex2char(char *);
static void hstrncpy(unsigned char *, char *, int);
static int  read_info_file(char *, void *, int);

/*static unsigned short calc_mb_crc __PR((unsigned char *, long, unsigned short));*/
static struct hfs_info *get_hfs_fe_info(struct hfs_info *, char *);
static struct hfs_info *get_hfs_sgi_info(struct hfs_info *, char *);
static struct hfs_info *match_key(struct hfs_info *, char *);

static int  get_hfs_itype(char *, char *, char *);
static void map_ext(char *, char **, char **, short *, char *);

static afpmap     **map;            /* list of mappings */
static afpmap     *defmap;    /* the default mapping */
static int  last_ent;   /* previous mapped entry */
static int  map_num;    /* number of mappings */
static int  mlen;       /* min extension length */
static char tmp[PATH_MAX];    /* tmp working buffer */
static int  hfs_num;    /* number of file types */
static char p_buf[PATH_MAX]; /* info working buffer */
static FILE *p_fp = NULL;     /* probe File pointer */
static int  p_num = 0;  /* probe bytes read */
static unsigned   int hselect;      /* type of HFS file selected */

struct hfs_type { /* Types of various HFS Unix files */
      int   type; /* type of file */
      int   flags;      /* special flags */
      char  *info;      /* finderinfo name */
      char  *rsrc;      /* resource fork name */
      int   (*get_info)(char *, char *, dir_ent *, int); /* finderinfo */
                                                    /*      function */
      int   (*get_dir)(char *, char *, dir_ent *, int);  /* directory */
                                                    /* name */
                                                    /* function */
      char  *desc;      /* description */
};

/* Above filled in */
static struct hfs_type hfs_types[] = {
      {TYPE_NONE, INSERT, "", "", get_none_info, get_none_dir, "None"},
      {TYPE_CAP, INSERT, ".finderinfo/", ".resource/",
                        get_cap_info, get_cap_dir, "CAP"},
      {TYPE_NETA, INSERT, ".AppleDouble/", ".AppleDouble/",
                        get_dbl_info, get_dbl_dir, "Netatalk"},
      {TYPE_DBL, INSERT, "%", "%", get_dbl_info, get_dbl_dir, "AppleDouble"},
      {TYPE_ESH, INSERT, ".rsrc/", ".rsrc/",
                        get_es_info, get_es_dir, "EtherShare/UShare"},
      {TYPE_FEU, NOPEND, "FINDER.DAT", "RESOURCE.FRK/",
                        get_fe_info, get_fe_dir, "Exchange"},
      {TYPE_FEL, NOPEND, "finder.dat", "resource.frk/",
                        get_fe_info, get_fe_dir, "Exchange"},
      {TYPE_SGI, NOPEND, ".HSancillary", ".HSResource/",
                        get_sgi_info, get_sgi_dir, "XINET/SGI"},
      {TYPE_MBIN, PROBE, "", "", get_mb_info, get_none_dir, "MacBinary"},
      {TYPE_SGL, PROBE, "", "", get_sgl_info, get_none_dir, "AppleSingle"},
      {TYPE_DAVE, INSERT, "resource.frk/", "resource.frk/",
                        get_dbl_info, get_dbl_dir, "DAVE"},
      {TYPE_SFM, APPEND | NORSRC, ":Afp_AfpInfo", ":Afp_Resource",
                        get_sfm_info, get_none_dir, "SFM"},
      {TYPE_XDBL, INSERT, "._", "._", get_dbl_info, get_dbl_dir,
                        "MacOS X AppleDouble"},
      {TYPE_XHFS, APPEND | NOINFO, "/rsrc", "/rsrc", get_xhfs_info, get_xhfs_dir,
                        "MacOS X HFS"}
};

/* used by get_magic_match() return */
static char tmp_type[CT_SIZE + 1],
            tmp_creator[CT_SIZE + 1];

#ifdef      __used__
/*
 *    An array useful for CRC calculations that use 0x1021 as the "seed"
 *    taken from mcvert.c modified by Jim Van Verth.
 */

static unsigned short mb_magic[] = {
      0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
      0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
      0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
      0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
      0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
      0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
      0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
      0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
      0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
      0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
      0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
      0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
      0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
      0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
      0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
      0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
      0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
      0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
      0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
      0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
      0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
      0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
      0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
      0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
      0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
      0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
      0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
      0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
      0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
      0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
      0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
      0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};

#endif      /* __used__ */

#ifndef     HAVE_STRCASECMP
static int
strcasecmp(const char *s1, const char *s2)
{
      while (tolower(*s1) == tolower(*s2)) {
            if (*s1 == 0)
                  return (0);
            s1++;
            s2++;
      }
      return (tolower(*s1) - tolower(*s2));
}
#endif

/*
 *    set_ct: set CREATOR and TYPE in hfs_ent
 *
 *    CREATOR and TYPE are padded with spaces if not CT_SIZE long
 */

static void
set_ct(hfsdirent *hfs_ent, char *c, char *t)
{
      memset(hfs_ent->u.file.type, ' ', CT_SIZE);
      memset(hfs_ent->u.file.creator, ' ', CT_SIZE);

      strncpy(hfs_ent->u.file.type, t, MIN(CT_SIZE, strlen(t)));
      strncpy(hfs_ent->u.file.creator, c, MIN(CT_SIZE, strlen(c)));

      hfs_ent->u.file.type[CT_SIZE] = '\0';
      hfs_ent->u.file.creator[CT_SIZE] = '\0';
}

/*
 *    cstrncopy: Cap Unix name to HFS name
 *
 *    ':' is replaced by '%' and string is terminated with '\0'
 */
static void
cstrncpy(char *t, char *f, int c)
{
      while (c-- && *f) {
            switch (*f) {
            case ':':
                  *t = '%';
                  break;
            default:
                  *t = *f;
                  break;
            }
            t++;
            f++;
      }

      *t = '\0';
}

/*
 * dehex()
 *
 * Given a hexadecimal digit in ASCII, return the integer representation.
 *
 *    Taken from linux/fs/hfs/trans.c by Paul H. Hargrove
 */
static unsigned char
dehex(char c)
{
      if ((c >= '0') && (c <= '9')) {
            return (c - '0');
      }
      if ((c >= 'a') && (c <= 'f')) {
            return (c - 'a' + 10);
      }
      if ((c >= 'A') && (c <= 'F')) {
            return (c - 'A' + 10);
      }
/*    return (0xff); */
      return (0);
}

static unsigned char
hex2char(char *s)
{
      unsigned char     i1;
      unsigned char     i2;
      unsigned char     o;

      if (strlen(++s) < 2)
            return (0);

      i1 = (unsigned char) s[0];
      i2 = (unsigned char) s[1];

      if (!isxdigit(i1) || !isxdigit(i2))
            return (0);

      o = (dehex(i1) << 4) & 0xf0;
      o |= (dehex(i2) & 0xf);

      return (o);
}


/*
 *    hstrncpy: Unix name to HFS name with special character
 *    translation.
 *
 *    "%xx" or ":xx" is assumed to be a "special" character and
 *    replaced by character code given by the hex characters "xx"
 *
 *    if "xx" is not a hex number, then it is left alone - except
 *    that ":" is replaced by "%"
 *
 */
static void
hstrncpy(unsigned char *t, char *f, int c)
{
      unsigned char     o;

      while (c-- && *f) {
            switch (*f) {
            case ':':
            case '%':
                  if ((o = hex2char(f)) == 0) {
                        *t = conv_charset('%', in_nls, hfs_onls);
                  } else {
                        *t = o;
                        f += 2;
                  }
                  break;
            default:
                  *t = conv_charset(*f, in_nls, hfs_onls);
                  break;
            }
            t++;
            f++;
      }

      *t = '\0';
}

/*
 *    basename: find just the filename with any directory component
 */
/*
      not used at the moment ...
static char
basename(a)
      char  *a;
{
      char  *b;

      if ((b = strchr(a, '/')))
            return (++b);
      else
            return (a);
}
*/

/*
 *    set_Dinfo: set directory info
 */
static void
set_Dinfo(byte *ptr, hfsdirent *ent)
{
      Dinfo *dinfo = (Dinfo *)ptr;

      /* finder flags */
      ent->fdflags = d_getw((unsigned char *) dinfo->frFlags);

      if (icon_pos) {
            ent->u.dir.rect.top =
            d_getw((unsigned char *) dinfo->frRect[0]);
            ent->u.dir.rect.left =
            d_getw((unsigned char *) dinfo->frRect[1]);
            ent->u.dir.rect.bottom =
            d_getw((unsigned char *) dinfo->frRect[2]);
            ent->u.dir.rect.right =
            d_getw((unsigned char *) dinfo->frRect[3]);

            ent->fdlocation.v =
            d_getw((unsigned char *) dinfo->frLocation[0]);
            ent->fdlocation.h =
            d_getw((unsigned char *) dinfo->frLocation[1]);

            ent->u.dir.view =
            d_getw((unsigned char *) dinfo->frView);

            ent->u.dir.frscroll.v =
            d_getw((unsigned char *) dinfo->frScroll[0]);
            ent->u.dir.frscroll.h =
            d_getw((unsigned char *) dinfo->frScroll[1]);

      } else {
            /*
             * clear HFS_FNDR_HASBEENINITED to have tidy desktop ??
             */
            ent->fdflags &= 0xfeff;
      }
}

/*
 *    set_Finfo: set file info
 */
static void
set_Finfo(byte *ptr, hfsdirent *ent)
{
      Finfo *finfo = (Finfo *)ptr;

      /* type and creator from finder info */
      set_ct(ent, finfo->fdCreator, finfo->fdType);

      /* finder flags */
      ent->fdflags = d_getw((unsigned char *) finfo->fdFlags);

      if (icon_pos) {
            ent->fdlocation.v =
            d_getw((unsigned char *) finfo->fdLocation[0]);
            ent->fdlocation.h =
            d_getw((unsigned char *) finfo->fdLocation[1]);
      } else {
            /*
             * clear HFS_FNDR_HASBEENINITED to have tidy desktop ??
             */
            ent->fdflags &= 0xfeff;
      }
}

/*
 *    get_none_dir: ordinary Unix directory
 */
static int
get_none_dir(char *hname, char *dname, dir_ent *s_entry, int ret)
{
      /* just copy the given name */
      hstrncpy((unsigned char *) (s_entry->hfs_ent->name),
                                          dname, HFS_MAX_FLEN);

      return (ret);
}

/*
 *    get_none_info: ordinary Unix file - try to map extension
 */
static int
get_none_info(char *hname, char *dname, dir_ent *s_entry, int ret)
{
      char        *t,
                  *c;
      hfsdirent   *hfs_ent = s_entry->hfs_ent;

      map_ext(dname, &t, &c, &s_entry->hfs_ent->fdflags, s_entry->whole_name);

      /* just copy the given name */
      hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN);

      set_ct(hfs_ent, c, t);

      return (ret);
}

/*
 *    read_info_file:   open and read a finderinfo file for an HFS file
 *                or directory
 */
static int
read_info_file(char *name, /* finderinfo filename */
                              void *info, /* info buffer */
                              int len           /* length of above */)
{
      FILE        *fp;
      int         num;

      /* clear out any old finderinfo stuf */
      memset(info, 0, len);

      if ((fp = fopen(name, "rb")) == NULL)
            return (-1);

      /* read and ignore if the file is short - checked later */
      num = fread(info, 1, len, fp);

      fclose(fp);

      return (num);
}

/*
 *    get_cap_dir: get the CAP name for a directory
 */
static int
get_cap_dir(char *hname, /* whole path */ 
                        char *dname, /* this dir name */
                        dir_ent *s_entry, /* directory entry */ 
                        int ret)
{
      FileInfo    info;       /* finderinfo struct */
      int         num = -1;   /* bytes read */
      hfsdirent   *hfs_ent = s_entry->hfs_ent;

      num = read_info_file(hname, &info, sizeof (FileInfo));

      /* check finder info is OK */
      if (num > 0 &&
            info.fi_magic1 == FI_MAGIC1 &&
            info.fi_magic == FI_MAGIC &&
            info.fi_bitmap & FI_BM_MACINTOSHFILENAME) {
            /* use the finderinfo name if it exists */
            cstrncpy((char *) (hfs_ent->name),
                        (char *) (info.fi_macfilename), HFS_MAX_FLEN);

            set_Dinfo(info.finderinfo, hfs_ent);

            return (ret);
      } else {
            /* otherwise give it it's Unix name */
            hstrncpy((unsigned char *) (s_entry->hfs_ent->name),
                                          dname, HFS_MAX_FLEN);
            return (TYPE_NONE);
      }
}

/*
**    get_cap_info:     get CAP finderinfo for a file
*/
static int
get_cap_info(char *hname, /* whole path */
                         char *dname, /* this dir name */
                         dir_ent *s_entry, /* directory entry */
                         int ret)
{
      FileInfo    info;       /* finderinfo struct */
      int         num = -1;   /* bytes read */
      hfsdirent   *hfs_ent = s_entry->hfs_ent;

      num = read_info_file(hname, &info, sizeof (info));

      /* check finder info is OK */
      if (num > 0 &&
            info.fi_magic1 == FI_MAGIC1 &&
            info.fi_magic == FI_MAGIC) {

            if (info.fi_bitmap & FI_BM_MACINTOSHFILENAME) {
                  /* use the finderinfo name if it exists */
                  cstrncpy((char *) (hfs_ent->name),
                        (char *) (info.fi_macfilename), HFS_MAX_FLEN);
            } else {
                  /* use Unix name */
                  hstrncpy((unsigned char *) (hfs_ent->name), dname,
                                                HFS_MAX_FLEN);
            }

            set_Finfo(info.finderinfo, hfs_ent);
#ifdef USE_MAC_DATES
            /*
             * set created/modified dates - these date should have already
             * been set from the Unix data fork dates. The finderinfo dates
             * are in Mac format - but we have to convert them back to Unix
             * for the time being
             */
            if ((info.fi_datemagic & FI_CDATE)) {
                  /* use libhfs routines to get correct byte order */
                  hfs_ent->crdate = d_toutime(d_getl(info.fi_ctime));
            }
            if (info.fi_datemagic & FI_MDATE) {
                  hfs_ent->mddate = d_toutime(d_getl(info.fi_mtime));
            }
#endif      /* USE_MAC_DATES */
      } else {
            /* failed to open/read finderinfo - so try afpfile mapping */
            if (verbose > 2) {
                  fprintf(stderr,
                        "warning: %s doesn't appear to be a %s file\n",
                        s_entry->whole_name, hfs_types[ret].desc);
            }
            ret = get_none_info(hname, dname, s_entry, TYPE_NONE);
      }

      return (ret);
}

/*
 *    get_es_dir: get EtherShare/UShare finderinfo for a directory
 *
 *    based on code from Jens-Uwe Mager (jum@helios.de) and Phil Sylvester
 *    <psylvstr@interaccess.com>
 */
static int
get_es_dir(char *hname, /* whole path */ 
                    char *dname, /* this dir name */
                    dir_ent *s_entry, /* directory entry */
                    int ret)
{
      es_FileInfo *einfo;           /* EtherShare info struct */
      us_FileInfo *uinfo;           /* UShare info struct */
      char        info[ES_INFO_SIZE];     /* finderinfo buffer */
      int         num = -1;   /* bytes read */
      hfsdirent   *hfs_ent = s_entry->hfs_ent;

      /*
       * the EtherShare and UShare file layout is the same, but they store
       * finderinfo differently
       */
      einfo = (es_FileInfo *) info;
      uinfo = (us_FileInfo *) info;

      num = read_info_file(hname, info, sizeof (info));

      /* check finder info for EtherShare finderinfo */
      if (num >= (int)sizeof (es_FileInfo) &&
            d_getl(einfo->magic) == ES_MAGIC &&
            d_getw(einfo->version) == ES_VERSION) {

            set_Dinfo(einfo->finderinfo, hfs_ent);

      } else if (num >= (int)sizeof (us_FileInfo)) {
            /*
             * UShare has no magic number, so we assume that this is a valid
             * info/resource file ...
             */

            set_Dinfo(uinfo->finderinfo, hfs_ent);

      } else {
            /* failed to open/read finderinfo - so try afpfile mapping */
            if (verbose > 2) {
                  fprintf(stderr,
                        "warning: %s doesn't appear to be a %s file\n",
                        s_entry->whole_name, hfs_types[ret].desc);
            }
            ret = get_none_dir(hname, dname, s_entry, TYPE_NONE);
            return (ret);
      }

      /* set name */
      hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN);

      return (ret);
}

/*
 *    get_es_info:      get EtherShare/UShare finderinfo for a file
 *
 *    based on code from Jens-Uwe Mager (jum@helios.de) and Phil Sylvester
 *    <psylvstr@interaccess.com>
 */
static int
get_es_info(char *hname, /* whole path */
                        char *dname, /* this dir name */
                        dir_ent *s_entry, /* directory entry */
                        int ret)
{
      es_FileInfo *einfo;           /* EtherShare info struct */
      us_FileInfo *uinfo;           /* UShare info struct */
      char        info[ES_INFO_SIZE];     /* finderinfo buffer */
      int         num = -1;   /* bytes read */
      hfsdirent   *hfs_ent = s_entry->hfs_ent;
      dir_ent           *s_entry1;

      /*
       * the EtherShare and UShare file layout is the same, but they store
       * finderinfo differently
       */
      einfo = (es_FileInfo *) info;
      uinfo = (us_FileInfo *) info;

      num = read_info_file(hname, info, sizeof (info));

      /* check finder info for EtherShare finderinfo */
      if (num >= (int)sizeof (es_FileInfo) &&
            d_getl(einfo->magic) == ES_MAGIC &&
            d_getw(einfo->version) == ES_VERSION) {

            set_Finfo(einfo->finderinfo, hfs_ent);

            /*
             * set create date - modified date set from the Unix
             * data fork date
             */

            hfs_ent->crdate = d_getl(einfo->createTime);

      } else if (num >= (int)sizeof (us_FileInfo)) {
            /*
             * UShare has no magic number, so we assume that this is a valid
             * info/resource file ...
             */

            set_Finfo(uinfo->finderinfo, hfs_ent);

            /* set create and modified date - if they exist */
            if (uinfo->ctime)
                  hfs_ent->crdate =
                        d_getl(uinfo->ctime);

            if (uinfo->mtime)
                  hfs_ent->mddate =
                        d_getl(uinfo->mtime);
      } else {
            /* failed to open/read finderinfo - so try afpfile mapping */
            if (verbose > 2) {
                  fprintf(stderr,
                        "warning: %s doesn't appear to be a %s file\n",
                        s_entry->whole_name, hfs_types[ret].desc);
            }
            ret = get_none_info(hname, dname, s_entry, TYPE_NONE);
            return (ret);
      }

      /* this should exist ... */
      if ((s_entry1 = s_entry->assoc) == NULL)
            perr("TYPE_ESH error - shouldn't happen!");

      /* set name */
      hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN);

      /* real rsrc file starts ES_INFO_SIZE bytes into the file */
      if (s_entry1->size <= ES_INFO_SIZE) {
            s_entry1->size = 0;
            hfs_ent->u.file.rsize = 0;
      } else {
            s_entry1->size -= ES_INFO_SIZE;
            hfs_ent->u.file.rsize = s_entry1->size;
            s_entry1->hfs_off = ES_INFO_SIZE;
      }

      set_733((char *) s_entry1->isorec.size, s_entry1->size);

      return (ret);
}

/*
 * calc_crc() --
 *   Compute the MacBinary II-style CRC for the data pointed to by p, with the
 *   crc seeded to seed.
 *
 *   Modified by Jim Van Verth to use the magic array for efficiency.
 */
#ifdef      __used__
static unsigned short
calc_mb_crc(unsigned char *p, long len, unsigned short seed)
{
      unsigned short    hold; /* crc computed so far */
      long        i;    /* index into data */

      hold = seed;      /* start with seed */
      for (i = 0; i < len; i++, p++) {
            hold ^= (*p << 8);
            hold = (hold << 8) ^ mb_magic[(unsigned char) (hold >> 8)];
      }

      return (hold);
}/* calc_mb_crc() */

#endif      /* __used__ */

static int
get_mb_info(char *hname, /* whole path */
                        char *dname, /* this dir name */
                        dir_ent *s_entry, /* directory entry */
                        int ret)
{
      mb_info           *info;            /* finderinfo struct */
      char        *c;
      char        *t;
      hfsdirent   *hfs_ent;
      dir_ent           *s_entry1;
      int         i;

#ifdef TEST_CODE
      unsigned short    crc_file,
                  crc_calc;

#endif

      info = (mb_info *) p_buf;

      /*
       * routine called twice for each file - first to check that it is a
       * valid MacBinary file, second to fill in the HFS info. p_buf holds
       * the required raw data and it *should* remain the same between the
       * two calls
       */
      if (s_entry == 0) {
            /*
             * test that the CRC is OK - not set for MacBinary I files (and
             * incorrect in some MacBinary II files!). If this fails, then
             * perform some other checks
             */

#ifdef TEST_CODE
            /* leave this out for the time being ... */
            if (p_num >= MB_SIZE && info->version == 0 && info->zero1 == 0) {
                  crc_calc = calc_mb_crc((unsigned char *) info, 124, 0);
                  crc_file = d_getw(info->crc);
#ifdef DEBUG
                  fprintf(stderr, "%s: file %d, calc %d\n", hname,
                                          crc_file, crc_calc);
#endif      /* DEBUG */
                  if (crc_file == crc_calc)
                        return (ret);
            }
#endif      /* TEST_CODE */

            /*
             * check some of the fields for a valid MacBinary file not
             * zero1 and zero2 SHOULD be zero - but some files incorrect
             */

/*        if (p_num < MB_SIZE || info->nlen > 63 || info->zero2 || */
            if (p_num < MB_SIZE || info->zero1 ||
                  info->zero2 || info->nlen > 63 ||
                  info->version || info->nlen == 0 || *info->name == 0)
                  return (TYPE_NONE);

            /* check that the filename is OKish */
            for (i = 0; i < (int)info->nlen; i++)
                  if (info->name[i] == 0)
                        return (TYPE_NONE);

            /* check CREATOR and TYPE are valid */
            for (i = 0; i < 4; i++)
                  if (info->type[i] == 0 || info->auth[i] == 0)
                        return (TYPE_NONE);
      } else {
            /* we have a vaild MacBinary file, so fill in the bits */

            /* this should exist ... */
            if ((s_entry1 = s_entry->assoc) == NULL)
                  perr("TYPE_MBIN error - shouldn't happen!");

            hfs_ent = s_entry->hfs_ent;

            /* type and creator from finder info */
            t = (char *) (info->type);
            c = (char *) (info->auth);

            set_ct(hfs_ent, c, t);

            /* finder flags */
            hfs_ent->fdflags = ((info->flags << 8) & 0xff00) | info->flags2;

            if (icon_pos) {
                  hfs_ent->fdlocation.v =
                        d_getw((unsigned char *) info->icon_vert);
                  hfs_ent->fdlocation.h =
                        d_getw((unsigned char *) info->icon_horiz);
            } else {
                  /*
                   * clear HFS_FNDR_HASBEENINITED to have tidy desktop ??
                   */
                  hfs_ent->fdflags &= 0xfeff;
            }

            /*
             * set created/modified dates - these date should have already
             * been set from the Unix data fork dates. The finderinfo dates
             * are in Mac format - but we have to convert them back to Unix
             * for the time being
             */
            hfs_ent->crdate = d_toutime(d_getl(info->cdate));
            hfs_ent->mddate = d_toutime(d_getl(info->mdate));

            /* set name */
            hstrncpy((unsigned char *) (hfs_ent->name),
                  (char *) (info->name), MIN(HFS_MAX_FLEN, info->nlen));

            /* set correct fork sizes */
            hfs_ent->u.file.dsize = d_getl(info->dflen);
            hfs_ent->u.file.rsize = d_getl(info->rflen);

            /* update directory entries for data fork */
            s_entry->size = hfs_ent->u.file.dsize;
            s_entry->hfs_off = MB_SIZE;
            set_733((char *) s_entry->isorec.size, s_entry->size);

            /*
             * real rsrc file starts after data fork (must be a multiple of
             * MB_SIZE)
             */
            s_entry1->size = hfs_ent->u.file.rsize;
            s_entry1->hfs_off = MB_SIZE + ROUND_UP(hfs_ent->u.file.dsize, MB_SIZE);
            set_733((char *) s_entry1->isorec.size, s_entry1->size);
      }

      return (ret);
}

/*
 *    get_dbl_dir:      get Apple double finderinfo for a directory
 *
 *    Based on code from cvt2cap.c (c) May 1988, Paul Campbell
 */
static int
get_dbl_dir(char *hname, /* whole path */
                        char *dname, /* this dir name */
                        dir_ent *s_entry, /* directory entry */
                        int ret)
{
      FileInfo    info;       /* finderinfo struct */
      a_hdr       *hp;
      a_entry           *ep;
      int         num = -1;   /* bytes read */
      int         nentries;
      FILE        *fp;
      hfsdirent   *hfs_ent = s_entry->hfs_ent;
      char        name[64];
      int         i;
      int         fail = 0;
      int         len = 0;

      hp = (a_hdr *) p_buf;
      memset(hp, 0, A_HDR_SIZE);

      memset(name, 0, sizeof (name));

      /* open and read the info/rsrc file (it's the same file) */
      if ((fp = fopen(hname, "rb")) != NULL)
            num = fread(hp, 1, A_HDR_SIZE, fp);

      /*
       * check finder info is OK - some Netatalk files don't have magic
       * or version set - ignore if it's a netatalk file
       */
      if (num == A_HDR_SIZE && ((ret == TYPE_NETA) ||
                  (d_getl(hp->magic) == APPLE_DOUBLE &&
                        (d_getl(hp->version) == A_VERSION1 ||
                              d_getl(hp->version) == A_VERSION2)))) {

            /* read TOC of the AppleDouble file */
            nentries = (int) d_getw(hp->nentries);
            if (fread(hp->entries, A_ENTRY_SIZE, nentries, fp) < 1) {
                  fail = 1;
                  nentries = 0;
            }
            /* extract what is needed */
            for (i = 0, ep = hp->entries; i < nentries; i++, ep++) {
                  switch ((int)d_getl(ep->id)) {
                  case ID_FINDER:
                        /* get the finder info */
                        fseek(fp, (off_t)d_getl(ep->offset), SEEK_SET);
                        if (fread(&info, d_getl(ep->length), 1, fp) < 1) {
                              fail = 1;
                        }
                        break;
                  case ID_NAME:
                        /* get Mac file name */
                        fseek(fp, (off_t)d_getl(ep->offset), SEEK_SET);
                        if (fread(name, d_getl(ep->length), 1, fp) < 1)
                              *name = '\0';
                        len = d_getl(ep->length);
                        break;
                  default:
                        break;
                  }
            }

            fclose(fp);

            /* skip this if we had a problem */
            if (!fail) {

                  set_Dinfo(info.finderinfo, hfs_ent);

                  /* use stored name if it exists */
                  if (*name) {
                        /*
                         * In some cases the name is stored in the
                         * Pascal string format - first char is the
                         * length, the rest is the actual string.
                         * The following *should* be OK
                         */
                        if (len == 32 && (int) name[0] < 32) {
                              cstrncpy(hfs_ent->name, &name[1],
                                    MIN(name[0], HFS_MAX_FLEN));
                        } else {
                              cstrncpy(hfs_ent->name, name,
                                          HFS_MAX_FLEN);
                        }
                  } else {
                        hstrncpy((unsigned char *) (hfs_ent->name),
                                          dname, HFS_MAX_FLEN);
                  }
            }
      } else {
            /* failed to open/read finderinfo */
            fail = 1;
            if (fp)
                  fclose(fp);
      }

      if (fail) {
            /* problem with the file - try mapping/magic */
            if (verbose > 2) {
                  fprintf(stderr,
                        "warning: %s doesn't appear to be a %s file\n",
                        s_entry->whole_name, hfs_types[ret].desc);
            }
            ret = get_none_dir(hname, dname, s_entry, TYPE_NONE);
      }
      return (ret);
}

/*
 *    Depending on the version, AppleDouble/Single stores dates
 *    relative to 1st Jan 1904 (v1) or 1st Jan 2000 (v2)
 *
 *    The d_toutime() function uses 1st Jan 1904 to convert to
 *    Unix time (1st Jan 1970).
 *
 *    The d_dtoutime() function uses 1st Jan 2000 to convert to
 *    Unix time (1st Jan 1970).
 *
 *    However, NetaTalk files seem to do their own thing - older
 *    Netatalk files don't have a magic number of version and
 *    store dates in ID=7 (don't know how). Newer Netatalk files
 *    claim to be version 1, but store dates in ID=7 as if they
 *    were version 2 files.
 */

/*
 *    get_dbl_info:     get Apple double finderinfo for a file
 *
 *    Based on code from cvt2cap.c (c) May 1988, Paul Campbell
 */
static int
get_dbl_info(char *hname, /* whole path */
                         char *dname, /* this dir name */
                         dir_ent *s_entry, /* directory entry */
                         int ret)
{
      FileInfo    info;       /* finderinfo struct */
      a_hdr       *hp;
      a_entry           *ep;
      int         num = -1;   /* bytes read */
      int         nentries;
      FILE        *fp;
      hfsdirent   *hfs_ent = s_entry->hfs_ent;
      dir_ent           *s_entry1;
      char        name[64];
      int         i;
      int         fail = 0;
      int         len = 0;
      unsigned char     dates[A_DATE];
      int         ver = 0, dlen;

      hp = (a_hdr *) p_buf;
      memset(hp, 0, A_HDR_SIZE);

      memset(name, 0, sizeof (name));
      memset(dates, 0, sizeof (dates));

      /* get the rsrc file info - should exist ... */
      if ((s_entry1 = s_entry->assoc) == NULL)
            perr("TYPE_DBL error - shouldn't happen!");

      /* open and read the info/rsrc file (it's the same file) */
      if ((fp = fopen(hname, "rb")) != NULL)
            num = fread(hp, 1, A_HDR_SIZE, fp);

      /*
       * check finder info is OK - some Netatalk files don't have magic
       * or version set - ignore if it's a netatalk file
       */

      ver = d_getl(hp->version);
      if (num == A_HDR_SIZE && ((ret == TYPE_NETA) ||
                  (d_getl(hp->magic) == APPLE_DOUBLE &&
                        (ver == A_VERSION1 || ver == A_VERSION2)))) {

            /* read TOC of the AppleDouble file */
            nentries = (int) d_getw(hp->nentries);
            if (fread(hp->entries, A_ENTRY_SIZE, nentries, fp) < 1) {
                  fail = 1;
                  nentries = 0;
            }
            /* extract what is needed */
            for (i = 0, ep = hp->entries; i < nentries; i++, ep++) {
                  switch ((int)d_getl(ep->id)) {
                  case ID_FINDER:
                        /* get the finder info */
                        fseek(fp, (off_t)d_getl(ep->offset), SEEK_SET);
                        if (fread(&info, d_getl(ep->length), 1, fp) < 1) {
                              fail = 1;
                        }
                        break;
                  case ID_RESOURCE:
                        /* set the offset and correct rsrc fork size */
                        s_entry1->size = d_getl(ep->length);
                        hfs_ent->u.file.rsize = s_entry1->size;
                        /* offset to start of real rsrc fork */
                        s_entry1->hfs_off = d_getl(ep->offset);
                        set_733((char *) s_entry1->isorec.size,
                                                s_entry1->size);
                        break;
                  case ID_NAME:
                        /* get Mac file name */
                        fseek(fp, (off_t)d_getl(ep->offset), SEEK_SET);
                        if (fread(name, d_getl(ep->length), 1, fp) < 1)
                              *name = '\0';
                        len = d_getl(ep->length);
                        break;
                  case ID_FILEI:
                        /* Workround for NetaTalk files ... */
                        if (ret == TYPE_NETA && ver == A_VERSION1)
                              ver = A_VERSION2;
                        /* fall through */
                  case ID_FILEDATESI:
                        /* get file info */
                        fseek(fp, d_getl(ep->offset), 0);
                        dlen = MIN(d_getl(ep->length), A_DATE);
                        if (fread(dates, dlen, 1, fp) < 1) {
                              fail = 1;
                        } else {
                              /* get the correct Unix time */
                              switch (ver) {

                              case (A_VERSION1):
                                    hfs_ent->crdate =
                                    d_toutime(d_getl(dates));
                                    hfs_ent->mddate =
                                    d_toutime(d_getl(dates+4));
                                    break;
                              case (A_VERSION2):
                                    hfs_ent->crdate =
                                    d_dtoutime(d_getl(dates));
                                    hfs_ent->mddate =
                                    d_dtoutime(d_getl(dates+4));
                                    break;
                              default:
                                    /* Use Unix dates */
                                    break;
                              }
                        }
                        break;
                  default:
                        break;
                  }
            }

            fclose(fp);

            /* skip this if we had a problem */
            if (!fail) {
                  set_Finfo(info.finderinfo, hfs_ent);

                  /* use stored name if it exists */
                  if (*name) {
                        /*
                         * In some cases the name is stored in the
                         * Pascal string format - first char is the
                         * length, the rest is the actual string.
                         * The following *should* be OK
                         */
                        if (len == 32 && (int) name[0] < 32) {
                              cstrncpy(hfs_ent->name, &name[1],
                                    MIN(name[0], HFS_MAX_FLEN));
                        } else {
                              cstrncpy(hfs_ent->name, name,
                                          HFS_MAX_FLEN);
                        }
                  } else {
                        hstrncpy((unsigned char *) (hfs_ent->name),
                                          dname, HFS_MAX_FLEN);
                  }
            }
      } else {
            /* failed to open/read finderinfo */
            fail = 1;
            if (fp)
                  fclose(fp);
      }

      if (fail) {
            /* problem with the file - try mapping/magic */
            if (verbose > 2) {
                  fprintf(stderr,
                        "warning: %s doesn't appear to be a %s file\n",
                        s_entry->whole_name, hfs_types[ret].desc);
            }
            ret = get_none_info(hname, dname, s_entry, TYPE_NONE);
      }
      return (ret);
}

/*
 *    get_sgl_info:     get Apple single finderinfo for a file
 *
 *    Based on code from cvt2cap.c (c) May 1988, Paul Campbell
 */
static int
get_sgl_info(char *hname, /* whole path */
                         char *dname, /* this dir name */
                         dir_ent *s_entry, /* directory entry */
                         int ret)
{
      FileInfo    *info = 0;  /* finderinfo struct */
      a_hdr       *hp;
      static a_entry    *entries;
      a_entry           *ep;
      int         nentries;
      hfsdirent   *hfs_ent;
      dir_ent           *s_entry1;
      char        name[64];
      int         i;
      int         len = 0;
      unsigned char     *dates;
      int         ver = 0;

      /*
       * routine called twice for each file
       * - first to check that it is a valid
       * AppleSingle file, second to fill in the HFS info.
       * p_buf holds the required
       * raw data and it *should* remain the same between the two calls
       */
      hp = (a_hdr *) p_buf;

      if (s_entry == 0) {
            if (p_num < A_HDR_SIZE ||
                  d_getl(hp->magic) != APPLE_SINGLE ||
                  (d_getl(hp->version) != A_VERSION1 &&
                        d_getl(hp->version) != A_VERSION2))
                  return (TYPE_NONE);

            /* check we have TOC for the AppleSingle file */
            nentries = (int) d_getw(hp->nentries);
            if (p_num < (int)(A_HDR_SIZE + nentries * A_ENTRY_SIZE))
                  return (TYPE_NONE);

            /* save the TOC */
            entries = (a_entry *) e_malloc(nentries * A_ENTRY_SIZE);

            memcpy(entries, (p_buf + A_HDR_SIZE), nentries * A_ENTRY_SIZE);
      } else {
            /* have a vaild AppleSingle File */
            memset(name, 0, sizeof (name));

            /* get the rsrc file info - should exist ... */
            if ((s_entry1 = s_entry->assoc) == NULL)
                  perr("TYPE_SGL error - shouldn't happen!");

            hfs_ent = s_entry->hfs_ent;

            nentries = (int) d_getw(hp->nentries);
            ver = d_getl(hp->version);

            /* extract what is needed */
            for (i = 0, ep = entries; i < nentries; i++, ep++) {
                  switch ((int)d_getl(ep->id)) {
                  case ID_FINDER:
                        /* get the finder info */
                        info = (FileInfo *) (p_buf + d_getl(ep->offset));
                        break;
                  case ID_DATA:
                        /* set the offset and correct data fork size */
                        hfs_ent->u.file.dsize = s_entry->size =
                                          d_getl(ep->length);
                        /* offset to start of real data fork */
                        s_entry->hfs_off = d_getl(ep->offset);
                        set_733((char *) s_entry->isorec.size,
                                                s_entry->size);
                        break;
                  case ID_RESOURCE:
                        /* set the offset and correct rsrc fork size */
                        hfs_ent->u.file.rsize = s_entry1->size =
                                          d_getl(ep->length);
                        /* offset to start of real rsrc fork */
                        s_entry1->hfs_off = d_getl(ep->offset);
                        set_733((char *) s_entry1->isorec.size,
                                                s_entry1->size);
                        break;
                  case ID_NAME:
                        /* get Mac file name */
                        strncpy(name, (p_buf + d_getl(ep->offset)),
                              d_getl(ep->length));
                        len = d_getl(ep->length);
                        break;
                  case ID_FILEI:
                        /* get file info - ignore at the moment*/
                        break;
                  case ID_FILEDATESI:
                        /* get file info */
                        dates = (unsigned char *)p_buf + d_getl(ep->offset);
                        /* get the correct Unix time */
                        if (ver == A_VERSION1) {
                              hfs_ent->crdate =
                                    d_toutime(d_getl(dates));
                              hfs_ent->mddate =
                                    d_toutime(d_getl(dates+4));
                        } else {
                              hfs_ent->crdate =
                                    d_dtoutime(d_getl(dates));
                              hfs_ent->mddate =
                                    d_dtoutime(d_getl(dates+4));
                        }
                        break;
                  default:
                        break;
                  }
            }

            free(entries);

            if (info == NULL) {
                  /*
                   * failed to open/read finderinfo
                   * - so try afpfile mapping
                   */
                  if (verbose > 2) {
                        fprintf(stderr,
                        "warning: %s doesn't appear to be a %s file\n",
                              s_entry->whole_name,
                              hfs_types[ret].desc);
                  }
                  ret = get_none_info(hname, dname, s_entry, TYPE_NONE);
                  return (ret);
            }

            set_Finfo(info->finderinfo, hfs_ent);

            /* use stored name if it exists */
            if (*name) {
                  /*
                   * In some cases the name is stored in the Pascal string
                   * format - first char is the length, the rest is the
                   * actual string. The following *should* be OK
                   */
                  if (len == 32 && (int) name[0] < 32) {
                        cstrncpy(hfs_ent->name, &name[1], MIN(name[0],
                                                HFS_MAX_FLEN));
                  } else {
                        cstrncpy(hfs_ent->name, name, HFS_MAX_FLEN);
                  }
            } else {
                  hstrncpy((unsigned char *) (hfs_ent->name), dname,
                                                HFS_MAX_FLEN);
            }
      }

      return (ret);
}

/*
 *    get_hfs_fe_info: read in the whole finderinfo for a PC Exchange
 *          directory - saves on reading this many times for each file.
 *
 *    Based of information provided by Mark Weinstein <mrwesq@earthlink.net>
 *
 *    Note: the FINDER.DAT file layout depends on the FAT cluster size
 *    therefore, files should only be read directly from the FAT media
 *
 *    Only tested with PC Exchange v2.1 - don't know if it will work
 *    with v2.2 and above.
 */
static struct hfs_info *
get_hfs_fe_info(struct hfs_info *hfs_info, char *name)
{
      FILE        *fp;
      int         fe_num,
                  fe_pad;
      fe_info           info;
      int         c = 0;
      struct hfs_info   *hfs_info1 = NULL;
      char        keyname[12];
      char        *s,
                  *e,
                  *k;
      int         i;

      if ((fp = fopen(name, "rb")) == NULL)
            return (NULL);

      /*
       * no longer attempt to find out FAT cluster
       * - rely on command line parameter
       */
      if (afe_size <= 0)
            return (NULL);

      fe_num = afe_size / FE_SIZE;
      fe_pad = afe_size % FE_SIZE;

      while (fread(&info, 1, FE_SIZE, fp) != 0) {

            /* the Mac name may be NULL - so ignore this entry */
            if (info.nlen != 0) {

                  hfs_info1 =
                  (struct hfs_info *)e_malloc(sizeof (struct hfs_info));
                  /* add this entry to the list */
                  hfs_info1->next = hfs_info;
                  hfs_info = hfs_info1;

                  /*
                   * get the bits we need
                   * - ignore [cm]time for the moment
                   */
                  cstrncpy(hfs_info->name, (char *) (info.name),
                              info.nlen);

                  memcpy(hfs_info->finderinfo, info.finderinfo, INFOLEN);

                  s = (char *) (info.sname);
                  e = (char *) (info.ext);
                  k = keyname;

                  /*
                   * short (Unix) name is stored in PC format,
                   * so needs to be mangled a bit
                   */

                  /* name part */
                  for (i = 0; i < 8; i++, s++, k++) {
                        if (*s == ' ')
                              break;
                        else
                              *k = *s;
                  }

                  /* extension - if it exists */
                  if (strncmp((const char *) (info.ext), "   ", 3)) {
                        *k = '.';
                        k++;
                        for (i = 0; i < 3; i++, e++, k++) {
                              if (*e == ' ')
                                    break;
                              else
                                    *k = *e;
                        }
                  }
                  *k = '\0';

                  hfs_info1->keyname = strdup(keyname);
            }
            /*
             * each record is FE_SIZE long, and there are FE_NUM
             * per each "cluster size", so we may need to skip the padding
             */
            if (++c == fe_num) {
                  c = 0;
                  fseek(fp, (off_t)fe_pad, SEEK_CUR);
            }
      }
      fclose(fp);

      return (hfs_info);
}

/*
 *    get_hfs_sgi_info: read in the whole finderinfo for a SGI (XINET)
 *          directory - saves on reading this many times for each
 *          file.
 */
static struct hfs_info *
get_hfs_sgi_info(struct hfs_info *hfs_info, char *name)
{
      FILE        *fp;
      sgi_info    info;
      struct hfs_info   *hfs_info1 = NULL;

      if ((fp = fopen(name, "rb")) == NULL)
            return (NULL);

      while (fread(&info, 1, SGI_SIZE, fp) != 0) {

            hfs_info1 = (struct hfs_info *)e_malloc(sizeof (struct hfs_info));
            /* add this entry to the list */
            hfs_info1->next = hfs_info;
            hfs_info = hfs_info1;

            /* get the bits we need - ignore [cm]time for the moment */
            cstrncpy(hfs_info->name, (char *)info.name, HFS_MAX_FLEN);

            memcpy(hfs_info->finderinfo, info.finderinfo, INFOLEN);

            /* use the HFS name as the key */
            hfs_info1->keyname = hfs_info->name;

      }
      fclose(fp);

      return (hfs_info);
}

/*
 *    del_hfs_info: delete the info list and recover memory
 */
void
del_hfs_info(struct hfs_info *hfs_info)
{
      struct hfs_info   *hfs_info1;

      while (hfs_info) {
            hfs_info1 = hfs_info;
            hfs_info = hfs_info->next;

            /* key may be the same as the HFS name - so don't free it */
            *hfs_info1->name = '\0';
            if (*hfs_info1->keyname)
                  free(hfs_info1->keyname);
            free(hfs_info1);
      }
}

/*
 *    match_key: find the correct hfs_ent using the Unix filename
 *          as the key
 */
static struct hfs_info *
match_key(struct hfs_info *hfs_info, char *key)
{
      while (hfs_info) {
            if (strcasecmp(key, hfs_info->keyname) == 0)
                  return (hfs_info);
            hfs_info = hfs_info->next;
      }

      return (NULL);
}

/*
 *    get_fe_dir: get PC Exchange directory name
 *
 *    base on probing with od ...
 */
static int
get_fe_dir(char *hname, /* whole path */
                    char *dname, /* this dir name */
                    dir_ent *s_entry, /* directory entry */
                    int ret)
{
      struct hfs_info   *hfs_info;
      hfsdirent   *hfs_ent = s_entry->hfs_ent;

      /* cached finderinfo stored with parent directory */
      hfs_info = s_entry->filedir->hfs_info;

      /* if we have no cache, then make one and store it */
      if (hfs_info == NULL) {
            if ((hfs_info = get_hfs_fe_info(hfs_info, hname)) == NULL)
                  ret = TYPE_NONE;
            else
                  s_entry->filedir->hfs_info = hfs_info;
      }
      if (ret != TYPE_NONE) {
            /* see if we can find the details of this file */
            if ((hfs_info = match_key(hfs_info, dname)) != NULL) {
                  strcpy(hfs_ent->name, hfs_info->name);

                  set_Dinfo(hfs_info->finderinfo, hfs_ent);

                  return (ret);
            }
      }
      /* can't find the entry, so use the Unix name */
      hstrncpy((unsigned char *)(hfs_ent->name), dname, HFS_MAX_FLEN);

      return (TYPE_NONE);
}

/*
 *    get_fe_info: get PC Exchange file details.
 *
 *    base on probing with od and details from Mark Weinstein
 *    <mrwesq@earthlink.net>
 */
static int
get_fe_info(char *hname, /* whole path */
                     char *dname, /* this dir name */
                     dir_ent *s_entry, /* directory entry */
                     int ret)
{
      struct hfs_info   *hfs_info;
      hfsdirent   *hfs_ent = s_entry->hfs_ent;

      /* cached finderinfo stored with parent directory */
      hfs_info = s_entry->filedir->hfs_info;

      /* if we have no cache, then make one and store it */
      if (hfs_info == NULL) {
            if ((hfs_info = get_hfs_fe_info(hfs_info, hname)) == NULL)
                  ret = TYPE_NONE;
            else
                  s_entry->filedir->hfs_info = hfs_info;
      }
      if (ret != TYPE_NONE) {
            char  *dn = dname;

#ifdef _WIN32_TEST
            /*
             * may have a problem here - v2.2 has long filenames,
             * but we need to key on the short filename,
             * so we need do go a bit of win32 stuff
             * ...
             */
            char  sname[1024];
            char  lname[1024];

            cygwin32_conv_to_full_win32_path(s_entry->whole_name, lname);

            if (GetShortPathName(lname, sname, sizeof (sname))) {
                  if (dn = strrchr(sname, '\\'))
                        dn++;
                  else
                        dn = sname;
            }
#endif      /* _WIN32 */

            /* see if we can find the details of this file */
            if ((hfs_info = match_key(hfs_info, dn)) != NULL) {

                  strcpy(hfs_ent->name, hfs_info->name);

                  set_Finfo(hfs_info->finderinfo, hfs_ent);

                  return (ret);
            }
      }
      /* no entry found - use extension mapping */
      if (verbose > 2) {
            fprintf(stderr, "warning: %s doesn't appear to be a %s file\n",
                  s_entry->whole_name, hfs_types[ret].desc);
      }
      ret = get_none_info(hname, dname, s_entry, TYPE_NONE);

      return (TYPE_NONE);
}

/*
 *    get_sgi_dir: get SGI (XINET) HFS directory name
 *
 *    base on probing with od ...
 */
static int
get_sgi_dir(char *hname, /* whole path */
                     char *dname, /* this dir name */
                     dir_ent *s_entry, /* directory entry */
                     int ret)
{
      struct hfs_info   *hfs_info;
      hfsdirent   *hfs_ent = s_entry->hfs_ent;

      /* cached finderinfo stored with parent directory */
      hfs_info = s_entry->filedir->hfs_info;

      /* if we haven't got a cache, then make one */
      if (hfs_info == NULL) {
            if ((hfs_info = get_hfs_sgi_info(hfs_info, hname)) == NULL)
                  ret = TYPE_NONE;
            else
                  s_entry->filedir->hfs_info = hfs_info;
      }
      /* find the matching entry in the cache */
      if (ret != TYPE_NONE) {
            /* key is (hopefully) the real Mac name */
            cstrncpy(tmp, dname, strlen(dname));
            if ((hfs_info = match_key(hfs_info, tmp)) != NULL) {
                  strcpy(hfs_ent->name, hfs_info->name);

                  set_Dinfo(hfs_info->finderinfo, hfs_ent);

                  return (ret);
            }
      }
      /* no entry found - use Unix name */
      hstrncpy((unsigned char *)(hfs_ent->name), dname, HFS_MAX_FLEN);

      return (TYPE_NONE);
}

/*
 *    get_sgi_info: get SGI (XINET) HFS finder info
 *
 *    base on probing with od ...
 */
static int
get_sgi_info(char *hname, /* whole path */
                      char *dname, /* this dir name */
                      dir_ent *s_entry, /* directory entry */
                      int ret)
{
      struct hfs_info   *hfs_info;
      hfsdirent   *hfs_ent = s_entry->hfs_ent;

      /* cached finderinfo stored with parent directory */
      hfs_info = s_entry->filedir->hfs_info;

      /* if we haven't got a cache, then make one */
      if (hfs_info == NULL) {
            if ((hfs_info = get_hfs_sgi_info(hfs_info, hname)) == NULL)
                  ret = TYPE_NONE;
            else
                  s_entry->filedir->hfs_info = hfs_info;
      }
      if (ret != TYPE_NONE) {
            /*
             * tmp is the same as hname here, but we don't need hname
             * anymore in this function  ...  see if we can find the
             * details of this file using the Unix name as the key
             */
            cstrncpy(tmp, dname, strlen(dname));
            if ((hfs_info = match_key(hfs_info, tmp)) != NULL) {

                  strcpy(hfs_ent->name, hfs_info->name);

                  set_Finfo(hfs_info->finderinfo, hfs_ent);

                  return (ret);
            }
      }
      /* no entry found, so try file extension */
      if (verbose > 2) {
            fprintf(stderr, "warning: %s doesn't appear to be a %s file\n",
                  s_entry->whole_name, hfs_types[ret].desc);
      }
      ret = get_none_info(hname, dname, s_entry, TYPE_NONE);

      return (TYPE_NONE);
}

/*
 *    get_sfm_info:     get SFM finderinfo for a file
 */

static byte sfm_magic[4] = {0x41, 0x46, 0x50, 0x00};
static byte sfm_version[4] = {0x00, 0x00, 0x01, 0x00};

static int
get_sfm_info(char *hname, /* whole path */
                      char *dname, /* this dir name */
                      dir_ent *s_entry, /* directory entry */
                      int ret)
{
      sfm_info    info; /* finderinfo struct */
      int         num = -1; /* bytes read */
      hfsdirent   *hfs_ent = s_entry->hfs_ent;

      num = read_info_file(hname, &info, sizeof (info));

      /* check finder info is OK */
      if (num == sizeof (info) &&
            !memcmp((char *)info.afpi_Signature, (char *)sfm_magic, 4) &&
            !memcmp((char *)info.afpi_Version, (char *)sfm_version, 4)) {
            /* use Unix name */
            hstrncpy((unsigned char *)(hfs_ent->name), dname, HFS_MAX_FLEN);

            set_Finfo(info.finderinfo, hfs_ent);

      } else {
            /* failed to open/read finderinfo - so try afpfile mapping */
            if (verbose > 2) {
                  fprintf(stderr,
                        "warning: %s doesn't appear to be a %s file\n",
                        s_entry->whole_name, hfs_types[ret].desc);
            }
            ret = get_none_info(hname, dname, s_entry, TYPE_NONE);
      }

      return (ret);
}

#ifdef IS_MACOS_X
/*
 *    get_xhfs_dir:     get MacOS X HFS finderinfo for a directory
 *
 *    Code ideas from 'hfstar' by Marcel Weiher marcel@metaobject.com
 *    and another GNU hfstar by Torres Vedras paulotex@yahoo.com
 *
 *    Here we are dealing with actual HFS files - not some encoding
 *    we have to use a system call to get the finderinfo
 *
 *    The file name here is the pseudo name for the resource fork
 */
static int
get_xhfs_dir(char *hname, /* whole path */
                      char *dname, /* this dir name */
                      dir_ent *s_entry, /* directory entry */
                      int ret)
{
      int         err;
      hfsdirent   *hfs_ent = s_entry->hfs_ent;
      attrinfo    ainfo;
      struct attrlist attrs;
      int         i;

      memset(&attrs, 0, sizeof (attrs));

      /* set flags we need to get info from getattrlist() */
      attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
      attrs.commonattr  = ATTR_CMN_CRTIME | ATTR_CMN_MODTIME |
                        ATTR_CMN_FNDRINFO;

      /* get the info */
      err = getattrlist(hname, &attrs, &ainfo, sizeof (ainfo), 0);

      if (err == 0) {
            /*
             * If the Finfo is blank then we assume it's not a
             * 'true' HFS directory ...
             */
            err = 1;
            for (i = 0; i < sizeof (ainfo.info); i++) {
                  if (ainfo.info[i] != 0) {
                        err = 0;
                        break;
                  }
            }
      }

      /* check finder info is OK */
      if (err == 0) {

            hstrncpy((unsigned char *) (s_entry->hfs_ent->name),
                                          dname, HFS_MAX_FLEN);

            set_Dinfo(ainfo.info, hfs_ent);

            return (ret);
      } else {
            /* otherwise give it it's Unix name */
            hstrncpy((unsigned char *) (s_entry->hfs_ent->name),
                                          dname, HFS_MAX_FLEN);
            return (TYPE_NONE);
      }
}

/*
 *    get_xhfs_info:    get MacOS X HFS finderinfo for a file
 *
 *    Code ideas from 'hfstar' by Marcel Weiher marcel@metaobject.com,
 *    another GNU hfstar by Torres Vedras paulotex@yahoo.com and
 *    hfspax by Howard Oakley howard@quercus.demon.co.uk
 *
 *    Here we are dealing with actual HFS files - not some encoding
 *    we have to use a system call to get the finderinfo
 *
 *    The file name here is the pseudo name for the resource fork
 */
static int
get_xhfs_info(char *hname, /* whole path */
                       char *dname, /* this dir name */
                       dir_ent *s_entry, /* directory entry */
                       int ret)
{
      int         err;
      hfsdirent   *hfs_ent = s_entry->hfs_ent;
      attrinfo    ainfo;
      struct attrlist attrs;
      int         i;
      int         size;

      memset(&attrs, 0, sizeof (attrs));

      /* set flags we need to get info from getattrlist() */
      attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
      attrs.commonattr  = ATTR_CMN_CRTIME | ATTR_CMN_MODTIME |
                        ATTR_CMN_FNDRINFO;

      /* get the info */
      err = getattrlist(hname, &attrs, &ainfo, sizeof (ainfo), 0);

      /* check finder info is OK */
      if (err == 0) {

            /*
             * If the Finfo is blank and the resource file is empty,
             * then we assume it's not a 'true' HFS file ...
             * There will be not associated file if the resource fork
             * is empty
             */

            if (s_entry->assoc == NULL) {
                  err = 1;
                  for (i = 0; i < sizeof (ainfo.info); i++) {
                        if (ainfo.info[i] != 0) {
                              err = 0;
                              break;
                        }
                  }
            }

            if (err == 0) {

                  /* use Unix name */
                  hstrncpy((unsigned char *) (hfs_ent->name), dname,
                                    HFS_MAX_FLEN);

                  set_Finfo(ainfo.info, hfs_ent);

                  /*
                   * dates have already been set - but we will
                   * set them here as well from the HFS info
                   * shouldn't need to check for byte order, as
                   * the source is HFS ... but we will just in case
                   */
                  hfs_ent->crdate = d_getl((byte *)&ainfo.ctime.tv_sec);

                  hfs_ent->mddate = d_getl((byte *)&ainfo.mtime.tv_sec);
            }

      }

      if (err) {
            /* not a 'true' HFS file - so try afpfile mapping */
#if 0
            /*
             * don't print a warning as we will get lots on HFS
             * file systems ...
             */
            if (verbose > 2) {
                  fprintf(stderr,
                        "warning: %s doesn't appear to be a %s file\n",
                        s_entry->whole_name, hfs_types[ret].desc);
            }
#endif
            ret = get_none_info(hname, dname, s_entry, TYPE_NONE);
      }

      return (ret);
}
#endif /* IS_MACOS_X */

/*
 *    get_hfs_itype: get the type of HFS info for a file
 */
static int
get_hfs_itype(char *wname, char *dname, char *htmp)
{
      int   wlen,
            i;
      int   no_type = TYPE_NONE;

      wlen = strlen(wname) - strlen(dname);

      /* search through the known types looking for matches */
      for (i = 1; i < hfs_num; i++) {
            /* skip the ones that we don't care about */
            if ((hfs_types[i].flags & PROBE) ||
                        *(hfs_types[i].info) == TYPE_NONE) {
                  continue;
            }

            strcpy(htmp, wname);

            /*
             * special case - if the info file doesn't exist
             * for a requested type, then remember the type -
             * we don't return here, as we _may_ find another type
             * so we save the type here in case - we will have
             * problems if more than one of this type ever exists ...
             */
            if (hfs_types[i].flags & NOINFO) {
                  no_type = i;
            } else {

                  /* append or insert finderinfo filename part */
                  if (hfs_types[i].flags & APPEND)
                        strcat(htmp, hfs_types[i].info);
                  else
                        sprintf(htmp + wlen, "%s%s", hfs_types[i].info,
                              (hfs_types[i].flags & NOPEND) ? "" : dname);

                  /* hack time ... Netatalk is a special case ... */
                  if (i == TYPE_NETA) {
                        strcpy(htmp, wname);
                        strcat(htmp, "/.AppleDouble/.Parent");
                  }

                  if (!access(htmp, R_OK))
                        return (hfs_types[i].type);
            }
      }

      return (no_type);
}

/*
 *    set_root_info: set the root folder hfs_ent from given file
 */
void
set_root_info(char *name)
{
      dir_ent           *s_entry;
      hfsdirent   *hfs_ent;
      int         i;

      s_entry = root->self;

      hfs_ent = (hfsdirent *) e_malloc(sizeof (hfsdirent));
      memset(hfs_ent, 0, sizeof (hfsdirent));

      /* make sure root has a valid hfs_ent */
      s_entry->hfs_ent = root->hfs_ent = hfs_ent;

      /* search for correct type of root info data */
      for (i = 1; i < hfs_num; i++) {
            if ((hfs_types[i].flags & PROBE) ||
                        (hfs_types[i].get_info == get_none_info))
                  continue;

            if ((*(hfs_types[i].get_dir))(name, "", s_entry, i) == i)
                  return;
      }
}


/*
 *    get_hfs_dir: set the HFS directory name
 */
int
get_hfs_dir(char *wname, char *dname, dir_ent *s_entry)
{
      int   type;

      /* get the HFS file type from the info file (if it exists) */
      type = get_hfs_itype(wname, dname, tmp);

      /* try to get the required info */
      type = (*(hfs_types[type].get_dir)) (tmp, dname, s_entry, type);

      return (type);
}

/*
 *    get_hfs_info: set the HFS info for a file
 */
int
get_hfs_info(char *wname, char *dname, dir_ent *s_entry)
{
      int   type,
            wlen,
            i;

      wlen = strlen(wname) - strlen(dname);

      /* we may already know the type of Unix/HFS file - so process */
      if (s_entry->hfs_type != TYPE_NONE) {

            type = s_entry->hfs_type;

            strcpy(tmp, wname);

            /* append or insert finderinfo filename part */
            if (hfs_types[type].flags & APPEND)
                  strcat(tmp, hfs_types[type].info);
            else
                  sprintf(tmp + wlen, "%s%s", hfs_types[type].info,
                        (hfs_types[type].flags & NOPEND) ? "" : dname);

            type = (*(hfs_types[type].get_info))(tmp, dname, s_entry, type);

            /* if everything is as expected, then return */
            if (s_entry->hfs_type == type)
                  return (type);
      }
      /* we don't know what type we have so, find out */
      for (i = 1; i < hfs_num; i++) {
            if ((hfs_types[i].flags & PROBE) ||
                        *(hfs_types[i].info) == TYPE_NONE) {
                  continue;
            }

            strcpy(tmp, wname);

            /* append or insert finderinfo filename part */
            if (hfs_types[i].flags & APPEND) {
                  strcat(tmp, hfs_types[i].info);
            } else {
                  sprintf(tmp + wlen, "%s%s", hfs_types[i].info,
                        (hfs_types[i].flags & NOPEND) ? "" : dname);
            }

            /* if the file exists - and not a type we've already tried */
            if (!access(tmp, R_OK) && i != s_entry->hfs_type) {
                  type = (*(hfs_types[i].get_info))(tmp, dname,
                                          s_entry, i);
                  s_entry->hfs_type = type;
                  return (type);
            }
      }

      /* nothing found, so just a Unix file */
      type = (*(hfs_types[TYPE_NONE].get_info))(wname, dname,
                                          s_entry, TYPE_NONE);

      return (type);
}

/*
 *    get_hfs_rname: set the name of the Unix rsrc file for a file
 *
 *    For the time being we ignore the 'NOINFO' flag - the only case
 *    at the moment is for MacOS X HFS files - for files the resource
 *    fork exists - so testing the "filename/rsrc" pseudo file as
 *    the 'info' filename is OK ...
 */
int
get_hfs_rname(char *wname, char *dname, char *rname)
{
      int   wlen,
            type,
            i;
      int   p_fd = -1;

      wlen = strlen(wname) - strlen(dname);

      /* try to find what sort of Unix HFS file type we have */
      for (i = 1; i < hfs_num; i++) {
            /* skip if don't want to probe the files - (default) */
            if (hfs_types[i].flags & PROBE)
                  continue;

            strcpy(rname, wname);

            /* if we have a different info file, the find out it's type */
            if (*(hfs_types[i].rsrc) && *(hfs_types[i].info)) {
                  /* first test the Info file */

                  /* append or insert finderinfo filename part */
                  if (hfs_types[i].flags & APPEND) {
                        strcat(rname, hfs_types[i].info);
                  } else {
                        sprintf(rname + wlen, "%s%s", hfs_types[i].info,
                              (hfs_types[i].flags & NOPEND) ?
                                                "" : dname);
                  }

                  /* if it exists, then check the Rsrc file */
                  if (!access(rname, R_OK)) {
                        if (hfs_types[i].flags & APPEND) {
                              sprintf(rname + wlen, "%s%s", dname,
                                    hfs_types[i].rsrc);
                        } else {
                              sprintf(rname + wlen, "%s%s",
                                    hfs_types[i].rsrc, dname);
                        }

                        /*
                         * for some types, a rsrc fork may not exist,
                         * so just return the current type
                         * in these cases
                         */
                        if (hfs_types[i].flags & NORSRC ||
                                          !access(rname, R_OK))
                              return (hfs_types[i].type);
                  }
            } else {
                  /*
                   * if we are probing,
                   * then have a look at the contents to find type
                   */
                  if (p_fd < 0) {
                        /* open file, if not already open */
                        if ((p_fd = open(wname,
                                    O_RDONLY | O_BINARY)) < 0) {
                              /* can't open it, then give up */
                              return (TYPE_NONE);
                        } else {
                              if ((p_num = read(p_fd, p_buf,
                                          sizeof (p_buf))) <= 0) {
                                    /*
                                     * can't read, or zero length
                                     * - give up
                                     */
                                    close(p_fd);
                                    return (TYPE_NONE);
                              }
                              /* get file pointer and close file */
                              p_fp = fdopen(p_fd, "rb");
                              close(p_fd);
                              if (p_fp == NULL)
                                    return (TYPE_NONE);
                        }
                  }
                  /*
                   * call routine to do the work
                   * - use the given dname as this
                   * is the name we may use on the CD
                   */
                  type = (*(hfs_types[i].get_info)) (rname, dname, 0, i);
                  if (type != 0) {
                        fclose(p_fp);
                        return (type);
                  }
                  if (p_fp) {
                        /*
                         * close file
                         * - just use contents of buffer next time
                         */
                        fclose(p_fp);
                        p_fp = NULL;
                  }
            }
      }

      return (0);
}

/*
 *    hfs_exclude: file/directory names that hold finder/resource
 *               information that we want to exclude from the tree.
 *               These files/directories are processed later ...
 */
int
hfs_exclude(char *d_name)
{
      /* we don't exclude "." and ".." */
      if (strcmp(d_name, ".") == 0)
            return (0);
      if (strcmp(d_name, "..") == 0)
            return (0);

      /* do not add the following to our list of dir entries */
      if (DO_CAP & hselect) {
            /* CAP */
            if (strcmp(d_name, ".finderinfo") == 0)
                  return (1);
            if (strcmp(d_name, ".resource") == 0)
                  return (1);
            if (strcmp(d_name, ".ADeskTop") == 0)
                  return (1);
            if (strcmp(d_name, ".IDeskTop") == 0)
                  return (1);
            if (strcmp(d_name, "Network Trash Folder") == 0)
                  return (1);
            /*
             * special case when HFS volume is mounted using Linux's hfs_fs
             * Brad Midgley <brad@pht.com>
             */
            if (strcmp(d_name, ".rootinfo") == 0)
                  return (1);
      }
      if (DO_ESH & hselect) {
            /* Helios EtherShare files */
            if (strcmp(d_name, ".rsrc") == 0)
                  return (1);
            if (strcmp(d_name, ".Desktop") == 0)
                  return (1);
            if (strcmp(d_name, ".DeskServer") == 0)
                  return (1);
            if (strcmp(d_name, ".Label") == 0)
                  return (1);
      }
      if (DO_DBL & hselect) {
      /* Apple Double */
            /*
             * special case when HFS volume is mounted using Linux's hfs_fs
             */
            if (strcmp(d_name, "%RootInfo") == 0)
                  return (1);
            /*
             * have to be careful here - a filename starting with '%'
             * may be vaild if the next two letters are a hex character -
             * unfortunately '%' 'digit' 'digit' may be a valid resource
             * file name ...
             */
            if (*d_name == '%')
                  if (hex2char(d_name) == 0)
                        return (1);
      }
      if (DO_NETA & hselect) {
            if (strcmp(d_name, ".AppleDouble") == 0)
                  return (1);
            if (strcmp(d_name, ".AppleDesktop") == 0)
                  return (1);
      }
      if ((DO_FEU & hselect) || (DO_FEL & hselect)) {
            /* PC Exchange */
            if (strcmp(d_name, "RESOURCE.FRK") == 0)
                  return (1);
            if (strcmp(d_name, "FINDER.DAT") == 0)
                  return (1);
            if (strcmp(d_name, "DESKTOP") == 0)
                  return (1);
            if (strcmp(d_name, "FILEID.DAT") == 0)
                  return (1);
            if (strcmp(d_name, "resource.frk") == 0)
                  return (1);
            if (strcmp(d_name, "finder.dat") == 0)
                  return (1);
            if (strcmp(d_name, "desktop") == 0)
                  return (1);
            if (strcmp(d_name, "fileid.dat") == 0)
                  return (1);
      }
      if (DO_SGI & hselect) {
            /* SGI */
            if (strcmp(d_name, ".HSResource") == 0)
                  return (1);
            if (strcmp(d_name, ".HSancillary") == 0)
                  return (1);
      }
      if (DO_DAVE & hselect) {
            /* DAVE */
            if (strcmp(d_name, "resource.frk") == 0)
                  return (1);
            if (strcmp(d_name, "DesktopFolderDB") == 0)
                  return (1);
      }
#ifndef _WIN32
      /*
       * NTFS streams are not "seen" as files,
       * so WinNT will not see these files -
       * so ignore - used for testing under Unix
       */
      if (DO_SFM & hselect) {
            /* SFM */
            char  *dn = strrchr(d_name, ':');

            if (dn) {
                  if (strcmp(dn, ":Afp_Resource") == 0)
                        return (1);
                  if (strcmp(dn, ":Comments") == 0)
                        return (1);
                  if (strcmp(dn, ":Afp_AfpInfo") == 0)
                        return (1);
            }
      }
#endif      /* _WIN32 */

      if (DO_XDBL & hselect) {
            /* XDB */
            if (strncmp(d_name, "._", 2) == 0)
                  return (1);
      }

      return (0);
}

/*
 *    print_hfs_info: print info about the HFS files.
 *
 */
void
print_hfs_info(dir_ent *s_entry)
{
      fprintf(stderr, "Name: %s\n", s_entry->whole_name);
      fprintf(stderr, "\tFile type: %s\n", hfs_types[s_entry->hfs_type].desc);
      fprintf(stderr, "\tHFS Name: %s\n", s_entry->hfs_ent->name);
      fprintf(stderr, "\tISO Name: %s\n", s_entry->isorec.name);
      fprintf(stderr, "\tCREATOR: %s\n", s_entry->hfs_ent->u.file.creator);
      fprintf(stderr, "\tTYPE:      %s\n", s_entry->hfs_ent->u.file.type);
}


/*
 *    hfs_init: sets up the mapping list from the afpfile as well
 *           the default mapping (with or without) an afpfile
 */
void
hfs_init(char *name, Ushort fdflags, Uint hfs_select)
{
      FILE  *fp;        /* File pointer */
      int   count = NUMMAP;   /* max number of entries */
      char  buf[PATH_MAX];    /* working buffer */
      afpmap      *amap;            /* mapping entry */
      char  *c,
            *t,
            *e;
      int   i;

      /* setup number of Unix/HFS filetype - we may wish to not bother */
      if (hfs_select) {
            hfs_num = sizeof (hfs_types) / sizeof (struct hfs_type);

            /*
             * code below needs to be tidied up
             * - most can be made redundant
             */
            for (i = 0; i < hfs_num; i++)
                  hfs_types[i].flags &= ~1;     /* 0xfffffffe */

            for (i = 1; i < hfs_num; i++)
                  if (!((1 << i) & hfs_select))
                        hfs_types[i].flags |= PROBE;

            hselect = hfs_select;
      } else
            hfs_num = hselect = 0;

#ifdef DEBUG
      for (i = 0; i < hfs_num; i++)
            fprintf(stderr, "type = %d flags = %d\n",
                              i, hfs_types[i].flags);
#endif      /* DEBUG */

      /* min length set to max to start with */
      mlen = PATH_MAX;

#ifdef USE_MAGIC
      /* initialise magic state */
      if (magic_filename) {
            magic_state = magic_open(MAGIC_ERROR);
            if (magic_state == NULL)
                  perr("failed to initialise libmagic");
            if (magic_load(magic_state, magic_filename) == -1) {
                  fprintf(stderr, "failed to open magic file: %s\n",
                        magic_error(magic_state));
                  exit(1);
            }
      }
#endif /* USE_MAGIC */

      /* set defaults */
      map_num = last_ent = 0;

      /* allocate memory for the default entry */
      defmap = (afpmap *) e_malloc(sizeof (afpmap));

      /* set default values */
      defmap->extn = DEFMATCH;

      /* make sure creator and type are 4 chars long */
      strcpy(defmap->type, BLANK);
      strcpy(defmap->creator, BLANK);

      e = deftype;
      t = defmap->type;

      while (*e && (e - deftype) < CT_SIZE)
            *t++ = *e++;

      e = defcreator;
      c = defmap->creator;

      while (*e && (e - defcreator) < CT_SIZE)
            *c++ = *e++;

      /* length is not important here */
      defmap->elen = 0;

      /* no flags */
      defmap->fdflags = fdflags;

      /* no afpfile - no mappings */
      if (*name == '\0') {
            map = NULL;
            return;
      }
      if ((fp = fopen(name, "r")) == NULL)
            perr("unable to open mapping file");

      map = (afpmap **) e_malloc(NUMMAP * sizeof (afpmap *));

      /* read afpfile line by line */
      while (fgets(buf, PATH_MAX, fp) != NULL) {
            /* ignore any comment lines */
            c = tmp;
            *c = '\0';
            if (sscanf(buf, "%1s", c) == EOF || *c == '#')
                  continue;

            /* increase list size if needed */
            if (map_num == count) {
                  count += NUMMAP;
                  map = (afpmap **)realloc(map, count * sizeof (afpmap *));
                  if (map == NULL)
                        perr("not enough memory");
            }
            /* allocate memory for this entry */
            amap = (afpmap *) e_malloc(sizeof (afpmap));

            t = amap->type;
            c = amap->creator;

            /* extract the info */
            if (sscanf(buf, "%s%*s%*1s%c%c%c%c%*1s%*1s%c%c%c%c%*1s",
                        tmp, c, c + 1, c + 2, c + 3,
                        t, t + 1, t + 2, t + 3) != 9) {
                  fprintf(stderr,
                        "error scanning afpfile %s - continuing", name);
                  free(amap);
                  continue;
            }
            /* copy the extension found */
            if ((amap->extn = (char *) strdup(tmp)) == NULL)
                  perr("not enough memory");

            /* set end-of-string */
            *(t + 4) = *(c + 4) = '\0';

            /* find the length of the extension */
            amap->elen = strlen(amap->extn);

            /* set flags */
            amap->fdflags = fdflags;

            /* see if we have the default creator/type */
            if (strcmp(amap->extn, DEFMATCH) == 0) {
                  /* get rid of the old default */
                  free(defmap);
                  /* make this the default */
                  defmap = amap;
                  continue;
            }
            /* update the smallest extension length */
            mlen = MIN(mlen, amap->elen);

            /* add entry to the list */
            map[map_num++] = amap;

      }

      /* free up some memory */
      if (map_num != count) {
            map = (afpmap **) realloc(map, map_num * sizeof (afpmap *));
            if (map == NULL)
                  perr("not enough memory");
      }
}

#ifdef USE_MAGIC
static int
try_map_magic(char *whole_name, char **type, /* set type */
                          char **creator /* set creator */)
{
      const char * ret = magic_file(magic_state, whole_name);

#ifdef DEBUG
      fprintf(stderr, "magic_file(magic_state, \"%s\"): %s\n",
            whole_name, ret ? ret : "NULL");
#endif
      /*
       * check that we found a match; ignore results in the
       * wrong format (probably due to libmagic's built-in rules)
       */
      if (ret && strcspn(ret, " ") == CT_SIZE
          && ret[CT_SIZE] == ' '
          && strcspn(ret + CT_SIZE + 1, " ") == CT_SIZE) {
            memcpy(tmp_type, ret, CT_SIZE);
            tmp_type[CT_SIZE] = 0;
            memcpy(tmp_creator, ret + CT_SIZE + 1, CT_SIZE);
            tmp_creator[CT_SIZE] = 0;
#ifdef DEBUG
            fprintf(stderr, "tmp_type = \"%s\"; tmp_creator = \"%s\"\n",
                  tmp_type, tmp_creator);
#endif
            *type = tmp_type;
            *creator = tmp_creator;
            return (1);
      }

      return (0);
}
#endif /* USE_MAGIC */

/*
 *    map_ext: map a files extension with the list to get type/creator
 */
static void
map_ext(char *name, /* filename */
              char **type, /* set type */
              char **creator, /* set creator */
              short *fdflags, /* set finder flags */
              char *whole_name)
{
      int   i;          /* loop counter */
      int   len;        /* filename length */
      afpmap      *amap;            /* mapping entry */
      const char  *ret;

      /* we don't take fdflags from the map or magic file */
      *fdflags = defmap->fdflags;

#ifdef USE_MAGIC
      /*
       * if we have a magic file and we want to search it first,
       * then try to get a match
       */
      if (magic_state && hfs_last == MAP_LAST
          && try_map_magic(whole_name, type, creator))
                        return;
#endif /* USE_MAGIC */

      len = strlen(name);

      /* have an afpfile and filename if long enough */
      if (map && len >= mlen) {
            /*
             * search through the list - we start where we left off
             * last time in case this file is of the same type as the
             * last one
             */
            for (i = 0; i < map_num; i++) {
                  amap = map[last_ent];

                  /* compare the end of the filename */
/*                if (strcmp((name+len - amap->elen), amap->extn) == 0) { */
                  if (strcasecmp((name+len - amap->elen), amap->extn) == 0) {
                        /* set the required info */
                        *type = amap->type;
                        *creator = amap->creator;
                        *fdflags = amap->fdflags;
                        return;
                  }
                  /*
                   * move on to the next entry - wrapping round
                   * if neccessary
                   */
                  last_ent++;
                  last_ent %= map_num;
            }
      }
      /*
       * if no matches are found, file name too short, or no afpfile,
       * then take defaults
       */
      *type = defmap->type;
      *creator = defmap->creator;

#ifdef USE_MAGIC
      /*
       * if we have a magic file and we haven't searched yet,
       * then try to get a match
       */
      if (magic_state && hfs_last == MAG_LAST)
            try_map_magic(whole_name, type, creator);
#endif /* USE_MAGIC */
}

void
delete_rsrc_ent(dir_ent *s_entry)
{
      dir_ent     *s_entry1 = s_entry->next;

      if (s_entry1 == NULL)
            return;

      s_entry->next = s_entry1->next;
      s_entry->assoc = NULL;

      free(s_entry1->name);
      free(s_entry1->whole_name);

      free(s_entry1);
}

void
clean_hfs()
{
      if (map)
            free(map);

      if (defmap)
            free(defmap);

#ifdef USE_MAGIC
      if (magic_state) {
            magic_close(magic_state);
            magic_state = NULL;
      }
#endif /* USE_MAGIC */
}

#endif      /* APPLE_HYB */

void
perr(char *a)
{
#ifdef      USE_LIBSCHILY
      if (a)
            comerr("%s\n", a);
      else
            comerr("<no error message given>\n");
#else
      if (a)
            fprintf(stderr, "mkhybrid: %s\n", a);
      perror("mkhybrid");
      exit(1);
#endif
}

Generated by  Doxygen 1.6.0   Back to index