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

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

/* @(#)write.c    1.88 06/02/01 joerg */
/* Parts from @(#)write.c     1.106 07/02/17 joerg */
/*
 * Program write.c - dump memory  structures to  file for iso9660 filesystem.
 *
 * Written by Eric Youngdale (1993).
 *
 * Copyright 1993 Yggdrasil Computing, Incorporated
 * Copyright (c) 1999-2003 J. Schilling
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* APPLE_HYB James Pearson j.pearson@ge.ucl.ac.uk 23/2/2000 */

#include <mconfig.h>
#include "genisoimage.h"
#include <timedefs.h>
#include <fctldefs.h>
#ifdef SORTING
#include "match.h"
#endif /* SORTING */
#include <errno.h>
#include <schily.h>
#ifdef DVD_VIDEO
#include "dvd_reader.h"
#include "dvd_file.h"
#include "ifo_read.h"
#endif
#ifdef APPLE_HYB
#include <ctype.h>
#endif

#ifdef      VMS
#include "vms.h"
#endif

/* Max number of sectors we will write at  one time */
#define     NSECT 16

/* Counters for statistics */

static int  table_size = 0;
static int  total_dir_size = 0;
static int  rockridge_size = 0;
static struct directory **pathlist;
static int  next_path_index = 1;
static int  sort_goof;

static int  is_rr_dir = 0;

struct output_fragment *out_tail;
struct output_fragment *out_list;

struct iso_primary_descriptor vol_desc;

void  set_721(char *pnt, unsigned int i);
void  set_722(char *pnt, unsigned int i);
void  set_723(char *pnt, unsigned int i);
void  set_731(char *pnt, unsigned int i);
void  set_732(char *pnt, unsigned int i);
void  set_733(char *pnt, unsigned int i);
int   get_731(char *p);
int   get_732(char *p);
int   get_733(char *p);
static      int   xawrite(void *buffer, int size, int count, FILE *file, 
                                            int submode, BOOL islast);
void  xfwrite(void *buffer, int size, int count, FILE *file, int submode, 
                          BOOL islast);
static      int   assign_directory_addresses(struct directory *node);
#ifdef APPLE_HYB
static      void  write_one_file(char *filename, off_t size, FILE *outfile, 
                                                            off_t off);
#else
static      void  write_one_file(char *filename, off_t size, FILE *outfile);
#endif
static      void  write_files(FILE *outfile);
#if 0
static      void  dump_filelist     __PR((void));
#endif
static      int   compare_dirs(const void *rr, const void *ll);
int   sort_directory(struct directory_entry **sort_dir, int rr);
static      int   root_gen(void);
static      BOOL  assign_file_addresses(struct directory *dpnt, BOOL isnest);
static      void  free_one_directory(struct directory *dpnt);
static      void  free_directories(struct directory *dpnt);
void  generate_one_directory(struct directory *dpnt, FILE *outfile);
static      void  build_pathlist(struct directory *node);
static      int   compare_paths(void const *r, void const *l);
static      int   generate_path_tables(void);
void  memcpy_max(char *to, char *from, int max);
void  outputlist_insert(struct output_fragment *frag);
static      int   file_write(FILE *outfile);
static      int   pvd_write(FILE *outfile);
static      int   xpvd_write(FILE *outfile);
static      int   evd_write(FILE *outfile);
static      int   vers_write(FILE *outfile);
static      int   graftcp(char *to, char *from, char *ep);
static      int   pathcp(char *to, char *from, char *ep);
static      int   pathtab_write(FILE *outfile);
static      int   exten_write(FILE *outfile);
int   oneblock_size(int starting_extent);
static      int   pathtab_size(int starting_extent);
static      int   startpad_size(int starting_extent);
static      int   interpad_size(int starting_extent);
static      int   endpad_size(int starting_extent);
static      int   file_gen(void);
static      int   dirtree_dump(void);
static      int   dirtree_fixup(int starting_extent);
static      int   dirtree_size(int starting_extent);
static      int   ext_size(int starting_extent);
static      int   dirtree_write(FILE *outfile);
static      int   dirtree_cleanup(FILE *outfile);
static      int   startpad_write(FILE *outfile);
static      int   interpad_write(FILE *outfile);
static      int   endpad_write(FILE *outfile);
#ifdef APPLE_HYB
static      int   hfs_pad;
static      int   hfs_get_parms(char *key);
static      void  hfs_file_gen(int start_extent);
static      void  gen_prepboot(void);
Ulong get_adj_size(int Csize);
int   adj_size(int Csize, int start_extent, int extra);
void  adj_size_other(struct directory *dpnt);
static      int   hfs_hce_write(FILE * outfile);
int   insert_padding_file(int size);
#endif      /* APPLE_HYB */

#ifdef SORTING
static      int   compare_sort(const void * rr, const void * ll);
static      void  reassign_link_addresses(struct directory * dpnt);
static      int   sort_file_addresses(void);
#endif /* SORTING */

/*
 * Routines to actually write the disc.  We write sequentially so that
 * we could write a tape, or write the disc directly
 */
#define     FILL_SPACE(X)     memset(vol_desc.X, ' ', sizeof (vol_desc.X))

void
set_721(char *pnt, unsigned int i)
{
      pnt[0] = i & 0xff;
      pnt[1] = (i >> 8) & 0xff;
}

void
set_722(char *pnt, unsigned int i)
{
      pnt[0] = (i >> 8) & 0xff;
      pnt[1] = i & 0xff;
}

void
set_723(char *pnt, unsigned int i)
{
      pnt[3] = pnt[0] = i & 0xff;
      pnt[2] = pnt[1] = (i >> 8) & 0xff;
}

void
set_731(char *pnt, unsigned int i)
{
      pnt[0] = i & 0xff;
      pnt[1] = (i >> 8) & 0xff;
      pnt[2] = (i >> 16) & 0xff;
      pnt[3] = (i >> 24) & 0xff;
}

void
set_732(char *pnt, unsigned int i)
{
      pnt[3] = i & 0xff;
      pnt[2] = (i >> 8) & 0xff;
      pnt[1] = (i >> 16) & 0xff;
      pnt[0] = (i >> 24) & 0xff;
}

void
set_733(char *pnt, unsigned int i)
{
      pnt[7] = pnt[0] = i & 0xff;
      pnt[6] = pnt[1] = (i >> 8) & 0xff;
      pnt[5] = pnt[2] = (i >> 16) & 0xff;
      pnt[4] = pnt[3] = (i >> 24) & 0xff;
}

int
get_731(char *p)
{
      return ((p[0] & 0xff)
            | ((p[1] & 0xff) << 8)
            | ((p[2] & 0xff) << 16)
            | ((p[3] & 0xff) << 24));
}

int
get_732(char *p)
{
      return ((p[3] & 0xff)
            | ((p[2] & 0xff) << 8)
            | ((p[1] & 0xff) << 16)
            | ((p[0] & 0xff) << 24));
}

int
get_733(char *p)
{
      return ((p[0] & 0xff)
            | ((p[1] & 0xff) << 8)
            | ((p[2] & 0xff) << 16)
            | ((p[3] & 0xff) << 24));
}

void
xfwrite(void *buffer, int size, int count, FILE *file, int submode, BOOL islast)
{
      /*
       * This is a hack that could be made better.
       * XXXIs this the only place?
       * It is definitely needed on Operating Systems that do not allow to
       * write files that are > 2GB. If the system is fast enough to be able
       * to feed 1400 KB/s writing speed of a DVD-R drive, use stdout.
       * If the system cannot do this reliable, you need to use this hacky
       * option.
       */
      static int  idx = 0;

#ifdef      XFWRITE_DEBUG
      if (count != 1 || (size % 2048) != 0)
            fprintf(stderr, "Count: %d, size: %d\n", count, size);
#endif

      if (split_output != 0 &&
            (idx == 0 || ftell(file) >= ((off_t)1024 * 1024 * 1024))) {
            char        nbuf[512];
            extern char *outfile;

            if (idx == 0)
                  unlink(outfile);
            sprintf(nbuf, "%s_%02d", outfile, idx++);
            file = freopen(nbuf, "wb", file);
            if (file == NULL) {
#ifdef      USE_LIBSCHILY
                  comerr("Cannot open '%s'.\n", nbuf);
#else
                  fprintf(stderr, "Cannot open '%s'.\n", nbuf);
                  exit(1);
#endif
            }
      }
      while (count) {
            int   got;

            seterrno(0);
            if (osecsize != 0)
                  got = xawrite(buffer, size, count, file, submode, islast);
            else
                  got = fwrite(buffer, size, count, file);

            if (got <= 0) {
#ifdef      USE_LIBSCHILY
                  comerr("cannot fwrite %d*%d\n", size, count);
#else
                  fprintf(stderr, "cannot fwrite %d*%d\n", size, count);
                  exit(1);
#endif
            }
            /*
             * This comment is in hope to prevent silly people from
             * e.g. SuSE (who did not yet learn C but believe that
             * they need to patch other peoples code) from changing the
             * next cast into an illegal lhs cast expression.
             * The cast below is the correct way to handle the problem.
             * The (void *) cast is to avoid a GCC warning like:
             * "warning: dereferencing type-punned pointer will break \
             * strict-aliasing rules"
             * which is wrong this code. (void *) introduces a compatible
             * intermediate type in the cast list.
             */
            count -= got;
            buffer = (void *)(((char *)buffer) + size * got);
      }
}

static int
xawrite(void *buffer, int size, int count, FILE *file, int submode, BOOL islast)
{
      register char     *p = buffer;
      register int      amt = size * count;
      register int      n;
      struct xa_subhdr subhdr[2];

      if (osecsize == 2048)
            return (fwrite(buffer, size, count, file));

      if (amt % 2048)
            comerrno(EX_BAD,
                  "Trying to write %d bytes (not a multiple of 2048).\n",
                  amt);

      subhdr[0].file_number         = subhdr[1].file_number       = 0;
      subhdr[0].channel_number      = subhdr[1].channel_number    = 0;
      subhdr[0].coding        = subhdr[1].coding            = 0;

      while (amt > 0) {
#ifdef      LATER
            if (submode < 0)
                  subhdr[0].sub_mode = subhdr[1].sub_mode = XA_SUBH_DATA;
            else
                  subhdr[0].sub_mode = subhdr[1].sub_mode = submode;
#else
            subhdr[0].sub_mode = subhdr[1].sub_mode = XA_SUBH_DATA;
#endif

            if ((amt <= 2048) && islast) {
                  subhdr[0].sub_mode = subhdr[1].sub_mode
                                    |= (XA_SUBH_EOR|XA_SUBH_EOF);
            }
            n = fwrite(&subhdr, sizeof (subhdr), 1, file);
            if (n <= 0)
                  return (n);

            n = fwrite(p, 2048, 1, file);
            if (n <= 0)
                  return (n);

            p += 2048;
            amt -= 2048;
      }
      return (1);
}

#ifdef APPLE_HYB
/*
 * use the deferred_write struct to store info about the hfs_boot_file
 */
static struct deferred_write mac_boot;

#endif      /* APPLE_HYB */
static struct deferred_write  *dw_head = NULL,
                        *dw_tail = NULL;

unsigned int      last_extent_written = 0;
static      Uint  path_table_index;
time_t      begun;

/*
 * We recursively walk through all of the directories and assign extent
 * numbers to them.  We have already assigned extent numbers to everything that
 * goes in front of them
 */
static int
assign_directory_addresses(struct directory *node)
{
      int         dir_size;
      struct directory *dpnt;

      dpnt = node;

      while (dpnt) {
            /* skip if it's hidden */
            if (dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) {
                  dpnt = dpnt->next;
                  continue;
            }
            /*
             * If we already have an extent for this (i.e. it came from a
             * multisession disc), then don't reassign a new extent.
             */
            dpnt->path_index = next_path_index++;
            if (dpnt->extent == 0) {
                  dpnt->extent = last_extent;
                  dir_size = ISO_BLOCKS(dpnt->size);

                  last_extent += dir_size;

                  /*
                   * Leave room for the CE entries for this directory.
                   * Keep them close to the reference directory so that
                   * access will be quick.
                   */
                  if (dpnt->ce_bytes) {
                        last_extent += ISO_BLOCKS(dpnt->ce_bytes);
                  }
            }
            if (dpnt->subdir) {
                  assign_directory_addresses(dpnt->subdir);
            }
            dpnt = dpnt->next;
      }
      return (0);
}

#ifdef APPLE_HYB
static void
write_one_file(char *filename, off_t size, FILE *outfile, off_t off)
#else
static void
write_one_file(char *filename, off_t size, FILE *outfile)
#endif      /* APPLE_HYB */
{
      /*
       * It seems that there are still stone age C-compilers
       * around.
       * The Metrowerks C found on BeOS/PPC does not allow
       * more than 32kB of local vars.
       * As we do not need to call write_one_file() recursively
       * we make buffer static.
       */
static      char        buffer[SECTOR_SIZE * NSECT];
      FILE        *infile;
      off_t       remain;
      int   use;

      char *mirror_name;
      unsigned char md5[16];
      int include_in_jigdo = list_file_in_jigdo(filename, size, &mirror_name, md5);

      if ((infile = fopen(filename, "rb")) == NULL) {
#ifdef      USE_LIBSCHILY
            comerr("cannot open '%s'\n", filename);
#else
#ifndef     HAVE_STRERROR
            fprintf(stderr, "cannot open '%s': (%d)\n",
                        filename, errno);
#else
            fprintf(stderr, "cannot open '%s': %s\n",
                        filename, strerror(errno));
#endif
            exit(1);
#endif
      }
#ifdef APPLE_HYB
      fseek(infile, off, SEEK_SET);
#endif      /* APPLE_HYB */
      remain = size;

      if (include_in_jigdo)
            write_jt_match_record(filename, mirror_name, SECTOR_SIZE, size, md5);

      while (remain > 0) {
            int   amt;

            use = (remain > SECTOR_SIZE * NSECT - 1 ?
                        NSECT * SECTOR_SIZE : remain);
            use = ISO_ROUND_UP(use);      /* Round up to nearest sector */
                                    /* boundary */
            memset(buffer, 0, use);
            seterrno(0);
            amt = fread(buffer, 1, use, infile);
            if (amt < use && amt < remain) {
                  /*
                   * Note that genisoimage is not star and no 100% archiver.
                   * We only detect file growth if the new size does not
                   * match 'use' at the last read.
                   */
                  if (geterrno() == 0) {
#ifdef      USE_LIBSCHILY
         comerrno(EX_BAD,
               "File '%s' did shrink.\n"
               "Files must not be changed while genisoimage runs!\n",
               filename);
#else
         fprintf(stderr,
               "File '%s' did shrink.\n"
               "Files must not be changed while genisoimage runs!\n",
               filename);
                        exit(EX_BAD);
#endif
                  }
#ifdef      USE_LIBSCHILY
                  comerr("Cannot read from '%s'\n", filename);
#else
                  fprintf(stderr, "Cannot read from '%s'\n", filename);
                  exit(1);
#endif
            }
            if (!include_in_jigdo)
                  jtwrite(buffer, use, 1,
                          XA_SUBH_DATA, remain <= (SECTOR_SIZE * NSECT));
            xfwrite(buffer, use, 1, outfile,
                        XA_SUBH_DATA, remain <= (SECTOR_SIZE * NSECT));
            last_extent_written += use / SECTOR_SIZE;
#if 0
            if ((last_extent_written % 1000) < use / SECTOR_SIZE) {
                  fprintf(stderr, "%d..", last_extent_written);
            }
#else
            if (verbose > 0 &&
                (int)(last_extent_written % (gui ? 500 : 5000)) <
                                          use / SECTOR_SIZE) {
                  time_t      now;
                  time_t      the_end;
                  double      frac;

                  time(&now);
                  frac = last_extent_written / (1.0 * last_extent);
                  the_end = begun + (now - begun) / frac;
#ifndef NO_FLOATINGPOINT
                  fprintf(stderr, "%6.2f%% done, estimate finish %s",
                        frac * 100., ctime(&the_end));
#else
                  fprintf(stderr, "%3d.%-02d%% done, estimate finish %s",
                        (int)(frac * 100.),
                        (int)((frac+.00005) * 10000.)%100,
                        ctime(&the_end));
#endif
                  fflush(stderr);
            }
#endif
            remain -= use;
      }
      fclose(infile);
}/* write_one_file(... */

static void
write_files(FILE *outfile)
{
      struct deferred_write   *dwpnt,
                        *dwnext;

      dwpnt = dw_head;
      while (dwpnt) {
/*#define DEBUG*/
#ifdef DEBUG
            fprintf(stderr,
            "The file name is %s and pad is %d, size is %lld and extent is %d\n",
                        dwpnt->name, dwpnt->pad,
                        (Llong)dwpnt->size, dwpnt->extent);
#endif
            if (dwpnt->table) {
                  jtwrite(dwpnt->table, ISO_ROUND_UP(dwpnt->size), 1, XA_SUBH_DATA, TRUE);
                  xfwrite(dwpnt->table, ISO_ROUND_UP(dwpnt->size), 1,
                                          outfile,
                                          XA_SUBH_DATA, TRUE);
                  last_extent_written += ISO_BLOCKS(dwpnt->size);
                  table_size += dwpnt->size;
/*                fprintf(stderr, "Size %lld ", (Llong)dwpnt->size); */
                  free(dwpnt->table);
                  dwpnt->table = NULL;
            } else {

#ifdef VMS
                  vms_write_one_file(dwpnt->name, dwpnt->size, outfile);
#else
#ifdef APPLE_HYB
                  write_one_file(dwpnt->name, dwpnt->size, outfile,
                                                dwpnt->off);
#else
                  write_one_file(dwpnt->name, dwpnt->size, outfile);
#endif      /* APPLE_HYB */
#endif
                  free(dwpnt->name);
                  dwpnt->name = NULL;
            }


#ifndef DVD_VIDEO
#define     dvd_video   0
#endif

#ifndef APPLE_HYB
#define     apple_hyb   0
#endif

#if   defined(APPLE_HYB) || defined(DVD_VIDEO)

            if (apple_hyb || dvd_video) {
                  /*
                   * we may have to pad out ISO files to work with HFS
                   * clump sizes
                   */
                  char  blk[SECTOR_SIZE];
                  Uint  i;

                  for (i = 0; i < dwpnt->pad; i++) {
                        jtwrite(blk, SECTOR_SIZE, 1, 0, FALSE);
                        xfwrite(blk, SECTOR_SIZE, 1, outfile, 0, FALSE);
                        last_extent_written++;
                  }
            }
#endif      /* APPLE_HYB || DVD_VIDEO */


            dwnext = dwpnt;
            dwpnt = dwpnt->next;
            free(dwnext);
            dwnext = NULL;
      }
}/* write_files(... */

#if 0
static void
dump_filelist()
{
      struct deferred_write *dwpnt;

      dwpnt = dw_head;
      while (dwpnt) {
            fprintf(stderr, "File %s\n", dwpnt->name);
            dwpnt = dwpnt->next;
      }
      fprintf(stderr, "\n");
}

#endif

static int
compare_dirs(const void *rr, const void *ll)
{
      char        *rpnt,
                  *lpnt;
      struct directory_entry **r,
                  **l;

      r = (struct directory_entry **) rr;
      l = (struct directory_entry **) ll;
      rpnt = (*r)->isorec.name;
      lpnt = (*l)->isorec.name;

#ifdef APPLE_HYB
      /*
       * resource fork MUST (not sure if this is true for HFS volumes) be
       * before the data fork - so force it here
       */
      if ((*r)->assoc && (*r)->assoc == (*l))
            return (1);
      if ((*l)->assoc && (*l)->assoc == (*r))
            return (-1);
#endif      /* APPLE_HYB */

      /* If the entries are the same, this is an error. */
      if (strcmp(rpnt, lpnt) == 0) {
#ifdef      USE_LIBSCHILY
            errmsgno(EX_BAD,
                  "Error: '%s' and '%s' have the same ISO9660 name '%s'.\n",
                  (*r)->whole_name, (*l)->whole_name,
                  rpnt);
#else
            fprintf(stderr,
                  "Error: '%s' and '%s' have the same ISO9660 name '%s'.\n",
                  (*r)->whole_name, (*l)->whole_name,
                  rpnt);
#endif
            sort_goof++;
      }
      /* Check we don't have the same RR name */
      if (use_RockRidge && !is_rr_dir) {
            /*
             * entries *can* have the same RR name in the "rr_moved"
             * directory so skip checks if we're in reloc_dir
             */
            if (!(strcmp((*r)->name, (*l)->name))) {
#ifdef      USE_LIBSCHILY
                  errmsgno(EX_BAD,
                  "Error: '%s' and '%s' have the same Rock Ridge name '%s'.\n",
                        (*r)->whole_name, (*l)->whole_name,
                        (*r)->name);
#else
                  fprintf(stderr,
                  "Error: '%s' and '%s' have the same Rock Ridge name '%s'.\n",
                        (*r)->whole_name, (*l)->whole_name,
                        (*r)->name);
#endif
                  sort_goof++;
            }
      }
      /*
       * Put the '.' and '..' entries on the head of the sorted list. For
       * normal ASCII, this always happens to be the case, but out of band
       * characters cause this not to be the case sometimes.
       * FIXME(eric) - these tests seem redundant, in that the name is never
       * assigned these values.  It will instead be \000 or \001, and thus
       * should always be sorted correctly.   I need to figure out why I
       * thought I needed this in the first place.
       */
#if 0
      if (strcmp(rpnt, ".") == 0)
            return (-1);
      if (strcmp(lpnt, ".") == 0)
            return (1);

      if (strcmp(rpnt, "..") == 0)
            return (-1);
      if (strcmp(lpnt, "..") == 0)
            return (1);
#else
      /*
       * The code above is wrong (as explained in Eric's comment), leading to
       * incorrect sort order iff the -L option ("allow leading dots") is in
       * effect and a directory contains entries that start with a dot.
       *  (TF, Tue Dec 29 13:49:24 CET 1998)
       */
      if ((*r)->isorec.name_len[0] == 1 && *rpnt == 0)
            return (-1);      /* '.' */
      if ((*l)->isorec.name_len[0] == 1 && *lpnt == 0)
            return (1);

      if ((*r)->isorec.name_len[0] == 1 && *rpnt == 1)
            return (-1);      /* '..' */
      if ((*l)->isorec.name_len[0] == 1 && *lpnt == 1)
            return (1);
#endif

      while (*rpnt && *lpnt) {
            if (*rpnt == ';' && *lpnt != ';')
                  return (-1);
            if (*rpnt != ';' && *lpnt == ';')
                  return (1);

            if (*rpnt == ';' && *lpnt == ';')
                  return (0);

            if (*rpnt == '.' && *lpnt != '.')
                  return (-1);
            if (*rpnt != '.' && *lpnt == '.')
                  return (1);

            if ((unsigned char) *rpnt < (unsigned char) *lpnt)
                  return (-1);
            if ((unsigned char) *rpnt > (unsigned char) *lpnt)
                  return (1);
            rpnt++;
            lpnt++;
      }
      if (*rpnt)
            return (1);
      if (*lpnt)
            return (-1);
      return (0);
}

/*
 * Function:            sort_directory
 *
 * Purpose:       Sort the directory in the appropriate ISO9660
 *                order.
 *
 * Notes:         Returns 0 if OK, returns > 0 if an error occurred.
 */
int
sort_directory(struct directory_entry **sort_dir, int rr)
{
      int         dcount = 0;
      int         xcount = 0;
      int         j;
      int         i,
                  len;
      struct directory_entry *s_entry;
      struct directory_entry **sortlist;

      /* need to keep a count of how many entries are hidden */
      s_entry = *sort_dir;
      while (s_entry) {
            if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY)
                  xcount++;
            dcount++;
            s_entry = s_entry->next;
      }

      if (dcount == 0) {
            return (0);
      }
      /* OK, now we know how many there are.  Build a vector for sorting. */
      sortlist = (struct directory_entry **)
            e_malloc(sizeof (struct directory_entry *) * dcount);

      j = dcount - 1;
      dcount = 0;
      s_entry = *sort_dir;
      while (s_entry) {
            if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) {
                  /* put any hidden entries at the end of the vector */
                  sortlist[j--] = s_entry;
            } else {
                  sortlist[dcount] = s_entry;
                  dcount++;
            }
            len = s_entry->isorec.name_len[0];
            s_entry->isorec.name[len] = 0;
            s_entry = s_entry->next;
      }

      /* Each directory is required to contain at least . and .. */
      if (dcount < 2) {
#ifdef      USE_LIBSCHILY
            errmsgno(EX_BAD,
                  "Directory size too small (. or .. missing ??%s)\n",
                  "?"); /* Try to avoid a GCC trigraph warning */
#else
            fprintf(stderr,
                  "Directory size too small (. or .. missing ??%s)\n",
                  "?"); /* Try to avoid a GCC trigraph warning */
#endif
            sort_goof = 1;

      } else {
            /* only sort the non-hidden entries */
            sort_goof = 0;
            is_rr_dir = rr;
#ifdef PROTOTYPES
            qsort(sortlist, dcount, sizeof (struct directory_entry *),
                  (int (*) (const void *, const void *)) compare_dirs);
#else
            qsort(sortlist, dcount, sizeof (struct directory_entry *),
                  compare_dirs);
#endif

            /*
             * Now reassemble the linked list in the proper sorted order
             * We still need the hidden entries, as they may be used in
             * the Joliet tree.
             */
            for (i = 0; i < dcount + xcount - 1; i++) {
                  sortlist[i]->next = sortlist[i + 1];
            }

            sortlist[dcount + xcount - 1]->next = NULL;
            *sort_dir = sortlist[0];
      }

      free(sortlist);
      sortlist = NULL;
      return (sort_goof);
}

static int
root_gen()
{
      init_fstatbuf();

      root_record.length[0] = 1 +
                  offsetof(struct iso_directory_record, name[0]);
      root_record.ext_attr_length[0] = 0;
      set_733((char *) root_record.extent, root->extent);
      set_733((char *) root_record.size, ISO_ROUND_UP(root->size));
      iso9660_date(root_record.date, root_statbuf.st_mtime);
      root_record.flags[0] = ISO_DIRECTORY;
      root_record.file_unit_size[0] = 0;
      root_record.interleave[0] = 0;
      set_723(root_record.volume_sequence_number, volume_sequence_number);
      root_record.name_len[0] = 1;
      return (0);
}

#ifdef SORTING
/*
 *    sorts deferred_write entries based on the sort weight
 */
static int
compare_sort(const void *rr, const void *ll)
{
      struct deferred_write   **r;
      struct deferred_write   **l;
      int               r_sort;
      int               l_sort;

      r = (struct deferred_write **) rr;
      l = (struct deferred_write **) ll;
      r_sort = (*r)->s_entry->sort;
      l_sort = (*l)->s_entry->sort;

      if (r_sort != l_sort)
            return (r_sort < l_sort ? 1 : -1);
      else
            return ((*r)->extent - (*l)->extent);
}

/*
 *    reassign start extents to files that are "hard links" to
 *    files that may have been sorted
 */
static void
reassign_link_addresses(struct directory *dpnt)
{
      struct directory_entry  *s_entry;
      struct file_hash  *s_hash;

      while (dpnt) {
            s_entry = dpnt->contents;
            for (s_entry = dpnt->contents; s_entry; s_entry = s_entry->next) {
                  /* link files have already been given the weight NOT_SORTED */
                  if (s_entry->sort == NOT_SORTED)
                  {
                        /* update the start extent */
                        s_hash = find_hash(s_entry->dev, s_entry->inode);
                        if (s_hash) {
                              set_733((char *) s_entry->isorec.extent,
                                          s_hash->starting_block);
                              s_entry->starting_block = s_hash->starting_block;
                        }
                  }

                  if (verbose > 2 && s_entry->size != 0) {
                        fprintf(stderr, "%8u %8u ",
                              s_entry->starting_block,
                              (unsigned int)(s_entry->starting_block + ISO_BLOCKS(s_entry->size) - 1));

                        if (s_entry->inode != TABLE_INODE) {
                              fprintf(stderr, "%s\n", s_entry->whole_name);
                        } else {
                              fprintf(stderr, "%s%s%s\n",
                                    s_entry->filedir->whole_name,
                                    SPATH_SEPARATOR, trans_tbl);
                        }
                  }
            }
            if (dpnt->subdir) {
                  reassign_link_addresses(dpnt->subdir);
            }

            dpnt = dpnt->next;
      }
}

/*
 *    sort files in order of the given sort weight
 */
static int
sort_file_addresses()
{
      struct deferred_write   *dwpnt;
      struct deferred_write   **sortlist;
      struct directory_entry  *s_entry;
      int               start_extent;
      int               num = 0;
      int               i;

      /* need to store start extents for linked files */
      flush_hash();

      /* find out how many files we have */
      dwpnt = dw_head;
      while (dwpnt) {
            num++;
            dwpnt = dwpnt->next;
      }

      /* return if we have none */
      if (num == 0) {
            return (1);
      }

      /* save the start extent of the first file */
      start_extent = dw_head->extent;

      /* set up vector to store entries */
      sortlist = (struct deferred_write **)
            e_malloc(sizeof (struct deferred_write *) * num);

      for (i = 0, dwpnt = dw_head; i < num; i++, dwpnt = dwpnt->next)
            sortlist[i] = dwpnt;

      /* sort the list */
#ifdef PROTOTYPES
      qsort(sortlist, num, sizeof (struct deferred_write *),
            (int (*)(const void *, const void *))compare_sort);
#else
      qsort(sortlist, num, sizeof (struct deferred_write *), compare_sort);
#endif

      /* reconstruct the linked list */
      for (i = 0; i < num-1; i++) {
            sortlist[i]->next = sortlist[i+1];
      }

      sortlist[num-1]->next = NULL;
      dw_head = sortlist[0];

      free(sortlist);

      /* set the new start extents for the sorted list */
      for (i = 0, dwpnt = dw_head; i < num; i++, dwpnt = dwpnt->next) {
            s_entry = dwpnt->s_entry;
            dwpnt->extent = s_entry->starting_block = start_extent;
            set_733((char *) s_entry->isorec.extent, start_extent);

            start_extent += ISO_BLOCKS(s_entry->size);
#ifdef DVD_VIDEO
            /*
             * Shouldn't this be done for every type of sort? Otherwise
             * we will loose every pad info we add if we sort the files
             */
            if (dvd_video) {
                  start_extent += dwpnt->pad;
            }
#endif /* DVD_VIDEO */

            /* cache start extents for any linked files */
            add_hash(s_entry);
      }

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



static BOOL
assign_file_addresses(struct directory *dpnt, BOOL isnest)
{
      struct directory *finddir;
      struct directory_entry *s_entry;
      struct file_hash *s_hash;
      struct deferred_write *dwpnt;
      char        whole_path[PATH_MAX];
#ifdef DVD_VIDEO
      char        dvd_path[PATH_MAX];
      title_set_info_t * title_set_info = NULL;
      char  *p;
#endif
      BOOL  ret = FALSE;

      while (dpnt) {
#ifdef DVD_VIDEO
            if (dvd_video && root == dpnt->parent &&
                ((p = strstr(dpnt->whole_name, "VIDEO_TS")) != 0)&&
                strcmp(p, "VIDEO_TS") == 0) {

                  int     maxlen = strlen(dpnt->whole_name)-8;
                  if (maxlen > (sizeof (dvd_path)-1))
                        maxlen = sizeof (dvd_path)-1;
                  strncpy(dvd_path, dpnt->whole_name, maxlen);
                  dvd_path[maxlen] = '\0';

#ifdef DEBUG
                  fprintf(stderr, "Found 'VIDEO_TS', the path is %s \n", dvd_path);
#endif
                  title_set_info = DVDGetFileSet(dvd_path);
                  if (title_set_info == 0) {
                        /*
                         * Do not switch off -dvd-video but let is fail later.
                         */
/*                      dvd_video = 0;*/
                        errmsgno(EX_BAD, "Unable to parse DVD-Video structures.\n");
                  } else {
                        ret = TRUE;
                  }
            }
#endif /* DVD_VIDEO */

            s_entry = dpnt->contents;
            for (s_entry = dpnt->contents; s_entry;
                                    s_entry = s_entry->next) {
                  /*
                   * If we already have an  extent for this entry, then
                   * don't assign a new one.  It must have come from a
                   * previous session on the disc.  Note that we don't
                   * end up scheduling the thing for writing either.
                   */
                  if (get_733(s_entry->isorec.extent) != 0) {
                        continue;
                  }
                  /*
                   * This saves some space if there are symlinks present
                   */
                  s_hash = find_hash(s_entry->dev, s_entry->inode);
                  if (s_hash) {
                        if (verbose > 2) {
                              fprintf(stderr, "Cache hit for '%s%s%s'\n", s_entry->filedir->de_name,
                                    SPATH_SEPARATOR,
                                    s_entry->name);
                        }
                        set_733((char *) s_entry->isorec.extent,
                                    s_hash->starting_block);
                        set_733((char *) s_entry->isorec.size,
                                    s_hash->size);
#ifdef SORTING
                        /* check for non-directory files */
                        if (do_sort && ((s_entry->isorec.flags[0] & ISO_DIRECTORY) == 0)) {
                              /* make sure the real file has the highest weighting */
                              s_hash->de->sort = MAX(s_entry->sort, s_hash->de->sort);
                              /* flag this as a potential non-sorted file */
                              s_entry->sort = NOT_SORTED;
                        }
#endif /* SORTING */
                        continue;
                  }
                  /*
                   * If this is for a directory that is not a . or
                   * a .. entry, then look up the information for the
                   * entry.  We have already assigned extents for
                   * directories, so we just need to fill in the blanks
                   * here.
                   */
                  if (strcmp(s_entry->name, ".") != 0 &&
                              strcmp(s_entry->name, "..") != 0 &&
                              s_entry->isorec.flags[0] & ISO_DIRECTORY) {
                        finddir = dpnt->subdir;
                        while (1 == 1) {
                              if (finddir->self == s_entry)
                                    break;
                              finddir = finddir->next;
                              if (!finddir) {
#ifdef      DVD_VIDEO
                                    if (title_set_info != 0) {
                                          DVDFreeFileSet(title_set_info);
                                    }
#endif
                                    comerrno(EX_BAD,
                                          "Fatal goof - could not find dir entry for '%s'\n",
                                          s_entry->name);
                              }
                        }
                        set_733((char *) s_entry->isorec.extent,
                                    finddir->extent);
                        s_entry->starting_block = finddir->extent;
                        s_entry->size = ISO_ROUND_UP(finddir->size);
                        total_dir_size += s_entry->size;
                        add_hash(s_entry);
                        set_733((char *) s_entry->isorec.size,
                                    ISO_ROUND_UP(finddir->size));
                        continue;
                  }
                  /*
                   * If this is . or .., then look up the relevant info
                   * from the tables.
                   */
                  if (strcmp(s_entry->name, ".") == 0) {
                        set_733((char *) s_entry->isorec.extent,
                                                dpnt->extent);

                        /*
                         * Set these so that the hash table has the
                         * correct information
                         */
                        s_entry->starting_block = dpnt->extent;
                        s_entry->size = ISO_ROUND_UP(dpnt->size);

                        add_hash(s_entry);
                        s_entry->starting_block = dpnt->extent;
                        set_733((char *) s_entry->isorec.size,
                                    ISO_ROUND_UP(dpnt->size));
                        continue;
                  }
                  if (strcmp(s_entry->name, "..") == 0) {
                        if (dpnt == root) {
                              total_dir_size += root->size;
                        }
                        set_733((char *) s_entry->isorec.extent,
                                          dpnt->parent->extent);

                        /*
                         * Set these so that the hash table has the
                         * correct information
                         */
                        s_entry->starting_block = dpnt->parent->extent;
                        s_entry->size =
                              ISO_ROUND_UP(dpnt->parent->size);

                        add_hash(s_entry);
                        s_entry->starting_block = dpnt->parent->extent;
                        set_733((char *) s_entry->isorec.size,
                              ISO_ROUND_UP(dpnt->parent->size));
                        continue;
                  }
                  /*
                   * Some ordinary non-directory file.  Just schedule
                   * the file to be written.  This is all quite
                   * straightforward, just make a list and assign
                   * extents as we go.  Once we get through writing all
                   * of the directories, we should be ready write out
                   * these files
                   */
                  if (s_entry->size) {
                        dwpnt = (struct deferred_write *)
                              e_malloc(sizeof (struct deferred_write));
                        /* save this directory entry for later use */
                        dwpnt->s_entry = s_entry;
                        /* set the initial padding to zero */
                        dwpnt->pad = 0;
#ifdef DVD_VIDEO
                        if (dvd_video && (title_set_info != 0)) {
                              int pad;

                              pad = DVDGetFilePad(title_set_info, s_entry->name);
                              if (pad < 0) {
                                    errmsgno(EX_BAD,
                                    "Implementation botch. Video pad for file %s is %d\n",
                                    s_entry->name, pad),
                                    comerrno(EX_BAD,
                                    "Either the *.IFO file is bad or you found a genisoimage bug.\n");
                              }
                              dwpnt->pad = pad;
                              if (verbose > 0 && pad != 0) {
                                    fprintf(stderr,
                                          "The pad was %d for file %s\n", dwpnt->pad, s_entry->name);
                              }
                        }
#endif /* DVD_VIDEO */
#ifdef APPLE_HYB
                        /*
                         * maybe an offset to start of the real
                         * file/fork
                         */
                        dwpnt->off = s_entry->hfs_off;
#else
                        dwpnt->off = (off_t)0;
#endif      /* APPLE_HYB */
                        if (dw_tail) {
                              dw_tail->next = dwpnt;
                              dw_tail = dwpnt;
                        } else {
                              dw_head = dwpnt;
                              dw_tail = dwpnt;
                        }
                        if (s_entry->inode == TABLE_INODE) {
                              dwpnt->table = s_entry->table;
                              dwpnt->name = NULL;
                              sprintf(whole_path, "%s%s%s",
                                    s_entry->filedir->whole_name,
                                    SPATH_SEPARATOR, trans_tbl);
                        } else {
                              dwpnt->table = NULL;
                              strcpy(whole_path, s_entry->whole_name);
                              dwpnt->name = strdup(whole_path);
                        }
                        dwpnt->next = NULL;
                        dwpnt->size = s_entry->size;
                        dwpnt->extent = last_extent;
                        set_733((char *) s_entry->isorec.extent,
                                                last_extent);
                        s_entry->starting_block = last_extent;
                        add_hash(s_entry);
                        last_extent += ISO_BLOCKS(s_entry->size);
#ifdef DVD_VIDEO
                        /* Shouldn't we always add the pad info? */
                        if (dvd_video) {
                              last_extent += dwpnt->pad;
                        }
#endif /* DVD_VIDEO */
                        if (verbose > 2 && !do_sort) {
                              fprintf(stderr, "%8d %8u %s\n",
                                    s_entry->starting_block,
                                    last_extent - 1, whole_path);
                        }
#ifdef DBG_ISO
                        if (ISO_BLOCKS(s_entry->size) > 500) {
                              fprintf(stderr,
                                    "Warning: large file '%s'\n",
                                    whole_path);
                              fprintf(stderr,
                                    "Starting block is %d\n",
                                    s_entry->starting_block);
                              fprintf(stderr,
                              "Reported file size is %lld\n",
                                    (Llong)s_entry->size);

                        }
#endif
#ifdef      NOT_NEEDED  /* Never use this code if you like to create a DVD */

                        if (last_extent > (800000000 >> 11)) {
                              /* More than 800Mb? Punt */
                              fprintf(stderr,
                              "Extent overflow processing file '%s'\n",
                                    whole_path);
                              fprintf(stderr,
                                    "Starting block is %d\n",
                                    s_entry->starting_block);
                              fprintf(stderr,
                              "Reported file size is %lld\n",
                                          (Llong)s_entry->size);
                              exit(1);
                        }
#endif
                        continue;
                  }
                  /*
                   * This is for zero-length files.  If we leave the
                   * extent 0, then we get screwed, because many readers
                   * simply drop files that have an extent of zero.
                   * Thus we leave the size 0, and just assign the
                   * extent number.
                   */
                  set_733((char *) s_entry->isorec.extent, last_extent);
            }
            if (dpnt->subdir) {
                  if (assign_file_addresses(dpnt->subdir, TRUE))
                        ret = TRUE;
            }
            dpnt = dpnt->next;
      }
#ifdef DVD_VIDEO
      if (title_set_info != NULL) {
            DVDFreeFileSet(title_set_info);
      }
      if (dvd_video && !ret && !isnest) {
            errmsgno(EX_BAD,
                  "Could not find correct 'VIDEO_TS' directory.\n");
      }
#endif /* DVD_VIDEO */
      return (ret);
} /* assign_file_addresses(... */

static void
free_one_directory(struct directory *dpnt)
{
      struct directory_entry *s_entry;
      struct directory_entry *s_entry_d;

      s_entry = dpnt->contents;
      while (s_entry) {
            s_entry_d = s_entry;
            s_entry = s_entry->next;

            if (s_entry_d->rr_attributes) {
                  free(s_entry_d->rr_attributes);
                  s_entry_d->rr_attributes = NULL;
            }
            if (s_entry_d->name != NULL) {
                  free(s_entry_d->name);
                  s_entry_d->name = NULL;
            }
            if (s_entry_d->whole_name != NULL) {
                  free(s_entry_d->whole_name);
                  s_entry_d->whole_name = NULL;
            }
#ifdef APPLE_HYB
            if (apple_both && s_entry_d->hfs_ent && !s_entry_d->assoc)
                  free(s_entry_d->hfs_ent);
#endif      /* APPLE_HYB */

            free(s_entry_d);
            s_entry_d = NULL;
      }
      dpnt->contents = NULL;
}/* free_one_directory(... */

static void
free_directories(struct directory *dpnt)
{
      while (dpnt) {
            free_one_directory(dpnt);
            if (dpnt->subdir)
                  free_directories(dpnt->subdir);
            dpnt = dpnt->next;
      }
}

void
generate_one_directory(struct directory *dpnt, FILE *outfile)
{
      unsigned int      ce_address = 0;
      char        *ce_buffer;
      unsigned int      ce_index = 0;
      unsigned int      ce_size;
      unsigned int      dir_index;
      char        *directory_buffer;
      int         new_reclen;
      struct directory_entry *s_entry;
      struct directory_entry *s_entry_d;
      unsigned int      total_size;

      total_size = ISO_ROUND_UP(dpnt->size);
      directory_buffer = (char *) e_malloc(total_size);
      memset(directory_buffer, 0, total_size);
      dir_index = 0;

      ce_size = ISO_ROUND_UP(dpnt->ce_bytes);
      ce_buffer = NULL;

      if (ce_size > 0) {
            ce_buffer = (char *) e_malloc(ce_size);
            memset(ce_buffer, 0, ce_size);

            ce_index = 0;

            /* Absolute byte address of CE entries for this directory */
            ce_address = last_extent_written + (total_size >> 11);
            ce_address = ce_address << 11;
      }
      s_entry = dpnt->contents;
      while (s_entry) {
            /* skip if it's hidden */
            if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) {
                  s_entry = s_entry->next;
                  continue;
            }
            /*
             * We do not allow directory entries to cross sector
             * boundaries. Simply pad, and then start the next entry at
             * the next sector
             */
            new_reclen = s_entry->isorec.length[0];
            if ((dir_index & (SECTOR_SIZE - 1)) + new_reclen >=
                                                SECTOR_SIZE) {
                  dir_index = ISO_ROUND_UP(dir_index);
            }
            memcpy(directory_buffer + dir_index, &s_entry->isorec,
                  offsetof(struct iso_directory_record, name[0]) +
                  s_entry->isorec.name_len[0]);
            dir_index += offsetof(struct iso_directory_record, name[0]) +
                  s_entry->isorec.name_len[0];

            /* Add the Rock Ridge attributes, if present */
            if (s_entry->rr_attr_size) {
                  if (dir_index & 1) {
                        directory_buffer[dir_index++] = 0;
                  }
                  /*
                   * If the RR attributes were too long, then write the
                   * CE records, as required.
                   */
                  if (s_entry->rr_attr_size != s_entry->total_rr_attr_size) {
                        struct iso_xa_dir_record *xadp;
                        unsigned char     *pnt;
                        int         len,
                                    nbytes;

                        /*
                         * Go through the entire record, first skip
                         * the XA record and then fix up the
                         * CE entries so that the extent and offset
                         * are correct
                         */
                        pnt = s_entry->rr_attributes;
                        len = s_entry->total_rr_attr_size;

                        if (len >= 14) {
                              xadp = (struct iso_xa_dir_record *)pnt;

                              if (xadp->signature[0] == 'X' && xadp->signature[1] == 'A' &&
                                                      xadp->reserved[0] == '\0') {
                                    len -= 14;
                                    pnt += 14;
                              }
                        }

                        while (len > 3) {
#ifdef DEBUG
                              if (ce_size <= 0) {
                                    fprintf(stderr,
                                    "Warning: ce_index(%d) && ce_address(%d) not initialized\n",
                                          ce_index, ce_address);
                              }
#endif

                              if (pnt[0] == 'C' && pnt[1] == 'E') {
                                    nbytes = get_733((char *) pnt + 20);

                                    if ((ce_index & (SECTOR_SIZE - 1)) + nbytes >=
                                          SECTOR_SIZE) {
                                          ce_index = ISO_ROUND_UP(ce_index);
                                    }
                                    set_733((char *) pnt + 4,
                                          (ce_address + ce_index) >> 11);
                                    set_733((char *) pnt + 12,
                                          (ce_address + ce_index) & (SECTOR_SIZE - 1));


                                    /*
                                     * Now store the block in the
                                     * ce buffer
                                     */
                                    memcpy(ce_buffer + ce_index,
                                          pnt + pnt[2], nbytes);
                                    ce_index += nbytes;
                                    if (ce_index & 1) {
                                          ce_index++;
                                    }
                              }
                              len -= pnt[2];
                              pnt += pnt[2];
                        }

                  }
                  rockridge_size += s_entry->total_rr_attr_size;
                  memcpy(directory_buffer + dir_index,
                        s_entry->rr_attributes,
                        s_entry->rr_attr_size);
                  dir_index += s_entry->rr_attr_size;
            }
            if (dir_index & 1) {
                  directory_buffer[dir_index++] = 0;
            }
            s_entry_d = s_entry;
            s_entry = s_entry->next;

            /*
             * Joliet doesn't use the Rock Ridge attributes, so we free
             * it here.
             */
            if (s_entry_d->rr_attributes) {
                  free(s_entry_d->rr_attributes);
                  s_entry_d->rr_attributes = NULL;
            }
      }

      if (dpnt->size != dir_index) {
#ifdef      USE_LIBSCHILY
            errmsgno(EX_BAD,
                  "Unexpected directory length %lld expected: %d '%s'\n",
                  (Llong)dpnt->size,
                  dir_index, dpnt->de_name);
#else
            fprintf(stderr,
                  "Unexpected directory length %lld expected: %d '%s'\n",
                  (Llong)dpnt->size,
                  dir_index, dpnt->de_name);
#endif
      }
      jtwrite(directory_buffer, total_size, 1, 0, FALSE);
      xfwrite(directory_buffer, total_size, 1, outfile, 0, FALSE);
      last_extent_written += total_size >> 11;
      free(directory_buffer);
      directory_buffer = NULL;

      if (ce_size > 0) {
            if (ce_index != dpnt->ce_bytes) {
#ifdef      USE_LIBSCHILY
                  errmsgno(EX_BAD,
                  "Continuation entry record length mismatch %d expected: %d.\n",
                        ce_index, dpnt->ce_bytes);
#else
                  fprintf(stderr,
                  "Continuation entry record length mismatch %d expected: %d.\n",
                        ce_index, dpnt->ce_bytes);
#endif
            }
            jtwrite(ce_buffer, ce_size, 1, 0, FALSE);
            xfwrite(ce_buffer, ce_size, 1, outfile, 0, FALSE);
            last_extent_written += ce_size >> 11;
            free(ce_buffer);
            ce_buffer = NULL;
      }
}/* generate_one_directory(... */

static void
build_pathlist(struct directory *node)
{
      struct directory *dpnt;

      dpnt = node;

      while (dpnt) {
            /* skip if it's hidden */
            if ((dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) == 0)
                  pathlist[dpnt->path_index] = dpnt;

            if (dpnt->subdir)
                  build_pathlist(dpnt->subdir);
            dpnt = dpnt->next;
      }
}/* build_pathlist(... */

static int
compare_paths(void const *r, void const *l)
{
      struct directory const *ll = *(struct directory * const *) l;
      struct directory const *rr = *(struct directory * const *) r;

      if (rr->parent->path_index < ll->parent->path_index) {
            return (-1);
      }
      if (rr->parent->path_index > ll->parent->path_index) {
            return (1);
      }
      return (strcmp(rr->self->isorec.name, ll->self->isorec.name));

}/* compare_paths(... */

static int
generate_path_tables()
{
      struct directory_entry *de = NULL;
      struct directory *dpnt;
      int         fix;
      int         i;
      int         j;
      int         namelen;
      char        *npnt;
      char        *npnt1;
      int         tablesize;

      /* First allocate memory for the tables and initialize the memory */
      tablesize = path_blocks << 11;
      path_table_m = (char *) e_malloc(tablesize);
      path_table_l = (char *) e_malloc(tablesize);
      memset(path_table_l, 0, tablesize);
      memset(path_table_m, 0, tablesize);

      /*
       * Now start filling in the path tables.  Start with root directory
       */

      path_table_index = 0;
      pathlist = (struct directory **) e_malloc(sizeof (struct directory *)
            * next_path_index);
      memset(pathlist, 0, sizeof (struct directory *) * next_path_index);
      build_pathlist(root);

      do {
            fix = 0;
#ifdef PROTOTYPES
            qsort(&pathlist[1], next_path_index - 1,
                  sizeof (struct directory *),
                  (int (*) (const void *, const void *)) compare_paths);
#else
            qsort(&pathlist[1], next_path_index - 1,
                  sizeof (struct directory *),
                  compare_paths);
#endif

            for (j = 1; j < next_path_index; j++) {
                  if (pathlist[j]->path_index != j) {
                        pathlist[j]->path_index = j;
                        fix++;
                  }
            }
      } while (fix);

      for (j = 1; j < next_path_index; j++) {
            dpnt = pathlist[j];
            if (!dpnt) {
#ifdef      USE_LIBSCHILY
                  comerrno(EX_BAD, "Entry %d not in path tables\n", j);
#else
                  fprintf(stderr, "Entry %d not in path tables\n", j);
                  exit(1);
#endif
            }
            npnt = dpnt->de_name;

            /* So the root comes out OK */
            if ((*npnt == 0) || (dpnt == root)) {
                  npnt = ".";
            }
            npnt1 = strrchr(npnt, PATH_SEPARATOR);
            if (npnt1) {
                  npnt = npnt1 + 1;
            }
            de = dpnt->self;
            if (!de) {
#ifdef      USE_LIBSCHILY
                  comerrno(EX_BAD,
                  "Fatal ISO9660 goof - directory has amnesia\n");
#else
                  fprintf(stderr,
                  "Fatal ISO9660 goof - directory has amnesia\n");
                  exit(1);
#endif
            }
            namelen = de->isorec.name_len[0];

            path_table_l[path_table_index] = namelen;
            path_table_m[path_table_index] = namelen;
            path_table_index += 2;

            set_731(path_table_l + path_table_index, dpnt->extent);
            set_732(path_table_m + path_table_index, dpnt->extent);
            path_table_index += 4;

            if (dpnt->parent->path_index > 0xffff) {
#ifdef      USE_LIBSCHILY
                  comerrno(EX_BAD,
                  "Unable to generate sane path tables - too many directories (%d)\n",
                        dpnt->parent->path_index);
#else
                  fprintf(stderr,
                  "Unable to generate sane path tables - too many directories (%d)\n",
                        dpnt->parent->path_index);
                  exit(1);
#endif
            }

            set_721(path_table_l + path_table_index,
                  dpnt->parent->path_index);
            set_722(path_table_m + path_table_index,
                  dpnt->parent->path_index);
            path_table_index += 2;

            for (i = 0; i < namelen; i++) {
                  path_table_l[path_table_index] = de->isorec.name[i];
                  path_table_m[path_table_index] = de->isorec.name[i];
                  path_table_index++;
            }
            if (path_table_index & 1) {
                  path_table_index++;     /* For odd lengths we pad */
            }
      }

      free(pathlist);
      pathlist = NULL;
      if (path_table_index != path_table_size) {
#ifdef      USE_LIBSCHILY
            errmsgno(EX_BAD,
                  "Path table lengths do not match %d expected: %d\n",
                  path_table_index,
                  path_table_size);
#else
            fprintf(stderr,
                  "Path table lengths do not match %d expected: %d\n",
                  path_table_index,
                  path_table_size);
#endif
      }
      return (0);
}/* generate_path_tables(... */

void
memcpy_max(char *to, char *from, int max)
{
      int   n = strlen(from);

      if (n > max) {
            n = max;
      }
      memcpy(to, from, n);

}/* memcpy_max(... */

void
outputlist_insert(struct output_fragment *frag)
{
      struct output_fragment *nfrag;

      nfrag = e_malloc(sizeof (*frag));
      movebytes(frag, nfrag, sizeof (*frag));
      nfrag->of_start_extent = 0;

      if (out_tail == NULL) {
            out_list = out_tail = nfrag;
      } else {
            out_tail->of_next = nfrag;
            out_tail = nfrag;
      }
}

static int
file_write(FILE *outfile)
{
      Uint  should_write;

#ifdef APPLE_HYB
      char  buffer[SECTOR_SIZE];

      memset(buffer, 0, sizeof (buffer));

      if (apple_hyb) {

            int   i;

            /*
             * write out padding to round up to HFS allocation block
             */
            for (i = 0; i < hfs_pad; i++) {
                  jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
                  xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
                  last_extent_written++;
            }
      }
#endif      /* APPLE_HYB */

      /*
       * OK, all done with that crap.  Now write out the directories. This is
       * where the fur starts to fly, because we need to keep track of each
       * file as we find it and keep track of where we put it.
       */
      should_write = last_extent - session_start;

      if (verbose > 2) {
#ifdef DBG_ISO
            fprintf(stderr,
                  "Total directory extents being written = %d\n",
                                          last_extent);
#endif

#ifdef APPLE_HYB
            if (apple_hyb)
                  fprintf(stderr,
                  "Total extents scheduled to be written (inc HFS) = %d\n",
                        last_extent - session_start);
            else
#endif      /* APPLE_HYB */

                  fprintf(stderr,
                        "Total extents scheduled to be written = %u\n",
                        last_extent - session_start);
      }
      /* Now write all of the files that we need. */
      write_files(outfile);

#ifdef APPLE_HYB
      /* write out extents/catalog/dt file */
      if (apple_hyb) {

            jtwrite(hce->hfs_ce, HFS_BLOCKSZ, hce->hfs_tot_size, 0, FALSE);
            xfwrite(hce->hfs_ce, HFS_BLOCKSZ, hce->hfs_tot_size, outfile, 0, FALSE);

            /* round up to a whole CD block */
            if (HFS_ROUND_UP(hce->hfs_tot_size) -
                              hce->hfs_tot_size * HFS_BLOCKSZ) {
                  jtwrite(buffer,
                        HFS_ROUND_UP(hce->hfs_tot_size) -
                        hce->hfs_tot_size * HFS_BLOCKSZ, 1, 0, FALSE);
                  xfwrite(buffer,
                        HFS_ROUND_UP(hce->hfs_tot_size) -
                        hce->hfs_tot_size * HFS_BLOCKSZ, 1, outfile, 0, FALSE);
            }
            last_extent_written += ISO_ROUND_UP(hce->hfs_tot_size *
                                    HFS_BLOCKSZ) / SECTOR_SIZE;

            /* write out HFS boot block */
            if (mac_boot.name)
                  write_one_file(mac_boot.name, mac_boot.size, outfile,
                                                mac_boot.off);
      }
#endif      /* APPLE_HYB */

      /* The rest is just fluff. */
      if (verbose == 0) {
            return (0);
      }
#ifdef APPLE_HYB
      if (apple_hyb) {
            fprintf(stderr,
                  "Total extents actually written (inc HFS) = %d\n",
                  last_extent_written - session_start);
            fprintf(stderr, "(Size of ISO volume = %d, HFS extra = %d)\n",
                  last_extent_written - session_start - hfs_extra,
                  hfs_extra);
      } else
#else
      fprintf(stderr, "Total extents actually written = %d\n",
            last_extent_written - session_start);
#endif      /* APPLE_HYB */

      /* Hard links throw us off here */
      if (should_write != (last_extent - session_start)) {
            fprintf(stderr,
            "Number of extents written not what was predicted.  Please fix.\n");
            fprintf(stderr, "Predicted = %d, written = %d\n",
                                    should_write, last_extent);
      }
      fprintf(stderr, "Total translation table size: %d\n", table_size);
      fprintf(stderr, "Total rockridge attributes bytes: %d\n",
                                    rockridge_size);
      fprintf(stderr, "Total directory bytes: %d\n", total_dir_size);
      fprintf(stderr, "Path table size(bytes): %d\n", path_table_size);

#ifdef DEBUG
      fprintf(stderr,
            "next extent, last_extent, last_extent_written %d %d %d\n",
            next_extent, last_extent, last_extent_written);
#endif

      return (0);

}/* iso_write(... */

/*
 * Function to write the PVD for the disc.
 */
static int
pvd_write(FILE *outfile)
{
      char        iso_time[17];
      int         should_write;
      struct tm   local;
      struct tm   gmt;


      time(&begun);

      local = *localtime(&begun);
      gmt = *gmtime(&begun);

      /*
       * There was a comment here about breaking in the year 2000.
       * That's not true, in 2000 tm_year == 100, so 1900+tm_year == 2000.
       */
      sprintf(iso_time, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d00",
            1900 + local.tm_year,
            local.tm_mon + 1, local.tm_mday,
            local.tm_hour, local.tm_min, local.tm_sec);

      local.tm_min -= gmt.tm_min;
      local.tm_hour -= gmt.tm_hour;
      local.tm_yday -= gmt.tm_yday;
      if (local.tm_yday < -2)       /* Hit new-year limit   */
            local.tm_yday = 1;      /* Local is GMT + 1 day */
      iso_time[16] = (local.tm_min + 60 *
                        (local.tm_hour + 24 * local.tm_yday)) / 15;

      /* Next we write out the primary descriptor for the disc */
      memset(&vol_desc, 0, sizeof (vol_desc));
      vol_desc.type[0] = ISO_VD_PRIMARY;
      memcpy(vol_desc.id, ISO_STANDARD_ID, sizeof (ISO_STANDARD_ID));
      vol_desc.version[0] = 1;

      memset(vol_desc.system_id, ' ', sizeof (vol_desc.system_id));
      memcpy_max(vol_desc.system_id, system_id, strlen(system_id));

      memset(vol_desc.volume_id, ' ', sizeof (vol_desc.volume_id));
      memcpy_max(vol_desc.volume_id, volume_id, strlen(volume_id));

      should_write = last_extent - session_start;
      set_733((char *) vol_desc.volume_space_size, should_write);
      set_723(vol_desc.volume_set_size, volume_set_size);
      set_723(vol_desc.volume_sequence_number, volume_sequence_number);
      set_723(vol_desc.logical_block_size, SECTOR_SIZE);

      /*
       * The path tables are used by DOS based machines to cache directory
       * locations
       */
      set_733((char *) vol_desc.path_table_size, path_table_size);
      set_731(vol_desc.type_l_path_table, path_table[0]);
      set_731(vol_desc.opt_type_l_path_table, path_table[1]);
      set_732(vol_desc.type_m_path_table, path_table[2]);
      set_732(vol_desc.opt_type_m_path_table, path_table[3]);

      /* Now we copy the actual root directory record */
      memcpy(vol_desc.root_directory_record, &root_record,
            offsetof(struct iso_directory_record, name[0]) + 1);

      /*
       * The rest is just fluff.  It looks nice to fill in many of these
       * fields, though.
       */
      FILL_SPACE(volume_set_id);
      if (volset_id)
            memcpy_max(vol_desc.volume_set_id, volset_id, strlen(volset_id));

      FILL_SPACE(publisher_id);
      if (publisher)
            memcpy_max(vol_desc.publisher_id, publisher, strlen(publisher));

      FILL_SPACE(preparer_id);
      if (preparer)
            memcpy_max(vol_desc.preparer_id, preparer, strlen(preparer));

      FILL_SPACE(application_id);
      if (appid)
            memcpy_max(vol_desc.application_id, appid, strlen(appid));

      FILL_SPACE(copyright_file_id);
      if (copyright)
            memcpy_max(vol_desc.copyright_file_id, copyright,
                  strlen(copyright));

      FILL_SPACE(abstract_file_id);
      if (abstract)
            memcpy_max(vol_desc.abstract_file_id, abstract,
                  strlen(abstract));

      FILL_SPACE(bibliographic_file_id);
      if (biblio)
            memcpy_max(vol_desc.bibliographic_file_id, biblio,
                  strlen(biblio));

      FILL_SPACE(creation_date);
      FILL_SPACE(modification_date);
      FILL_SPACE(expiration_date);
      FILL_SPACE(effective_date);
      vol_desc.file_structure_version[0] = 1;
      FILL_SPACE(application_data);

      memcpy(vol_desc.creation_date, iso_time, 17);
      memcpy(vol_desc.modification_date, iso_time, 17);
      memcpy(vol_desc.expiration_date, "0000000000000000", 17);
      memcpy(vol_desc.effective_date, iso_time, 17);

      if (use_XA) {
            char  *xap = &((char *)&vol_desc)[1024];

            memcpy(&xap[0], "CD-XA001", 8);                 /* XA Sign.  */
            memcpy(&xap[8], "\0\0", 2);               /* XA flags  */
            memcpy(&xap[10], "\0\0\0\0\0\0\0\0", 8);  /* Start dir */
            memcpy(&xap[18], "\0\0\0\0\0\0\0\0", 8);  /* Reserved  */
      }

      /* if not a bootable cd do it the old way */
      jtwrite(&vol_desc, SECTOR_SIZE, 1, 0, FALSE);
      xfwrite(&vol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
      last_extent_written++;
      return (0);
}

/*
 * Function to write the Extended PVD for the disc.
 */
static int
xpvd_write(FILE *outfile)
{
      vol_desc.type[0] = ISO_VD_SUPPLEMENTARY;
      vol_desc.version[0] = 2;
      vol_desc.file_structure_version[0] = 2;

      /* if not a bootable cd do it the old way */
      jtwrite(&vol_desc, SECTOR_SIZE, 1, 0, FALSE);
      xfwrite(&vol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
      last_extent_written++;
      return (0);
}

/*
 * Function to write the EVD for the disc.
 */
static int
evd_write(FILE *outfile)
{
      struct iso_primary_descriptor evol_desc;

      /*
       * Now write the end volume descriptor.  Much simpler than the other
       * one
       */
      memset(&evol_desc, 0, sizeof (evol_desc));
      evol_desc.type[0] = (unsigned char) ISO_VD_END;
      memcpy(evol_desc.id, ISO_STANDARD_ID, sizeof (ISO_STANDARD_ID));
      evol_desc.version[0] = 1;
      jtwrite(&evol_desc, SECTOR_SIZE, 1, 0, TRUE);
      xfwrite(&evol_desc, SECTOR_SIZE, 1, outfile, 0, TRUE);
      last_extent_written += 1;
      return (0);
}

/*
 * Function to write the version information for the disc.
 */
static int
vers_write(FILE *outfile)
{
      char        vers[SECTOR_SIZE+1];
      int         X_ac;
      char        **X_av;
      char        *cp;
      int         i;
      int         idx = 4;
      int         len;
      extern char version_string[];
      extern int  path_ind;

      /* Now write the version descriptor. */
      memset(vers, 0, sizeof (vers));
      strcpy(vers, "MKI ");

      cp = vers;
      X_ac = saved_ac();
      X_av = saved_av();
      strcpy(&cp[idx], ctime(&begun));
      idx += 25;
      strcpy(&cp[idx], version_string);
      idx += strlen(version_string);
      for (i = 1; i < X_ac; i++) {
            len = strlen(X_av[i]);
            if ((idx + len + 2) >= SECTOR_SIZE)
                  break;
            cp[idx++] = ' ';
            /*
             * Do not give away secret information when not in debug mode.
             */
            if (debug)
                  strcpy(&cp[idx], X_av[i]);
            else if (i >= path_ind)
                  len = graftcp(&cp[idx], X_av[i], &vers[SECTOR_SIZE-1]);
            else if (X_av[i][0] == '/')
                  len = pathcp(&cp[idx], X_av[i], &vers[SECTOR_SIZE-1]);
            else
                  strcpy(&cp[idx], X_av[i]);
            idx += len;
      }

      cp[SECTOR_SIZE - 1] = '\0';
      /* Per default: keep privacy. Blackout the version and arguments. */
      if(getenv("ISODEBUG")) {
            jtwrite(vers, SECTOR_SIZE, 1, 0, TRUE);
            xfwrite(vers, SECTOR_SIZE, 1, outfile, 0, TRUE);
      } else {
            jtwrite(calloc(SECTOR_SIZE, 1), SECTOR_SIZE, 1, 0, TRUE);
            xfwrite(calloc(SECTOR_SIZE, 1), SECTOR_SIZE, 1, outfile, 0, TRUE);
      }
    last_extent_written += 1;
      return (0);
}

/*
 * Avoid to write unwanted information into the version info string.
 */
static int
graftcp(char *to, char *from, char *ep)
{
      int   len = strlen(from);
      char  *node = NULL;

      if (use_graft_ptrs)
            node = findgequal(from);

      if (node == NULL) {
            len = 0;
            node = from;
      } else {
            len = node - from;
            *node = '\0';
            strncpy(to, from, ep - to);
            *node++ = '=';
            to += len++;
            *to++ = '=';
      }
      return (len + pathcp(to, node, ep));
}

static int
pathcp(char *to, char *from, char *ep)
{
      int   len = strlen(from);
      char  *p;

      p = strrchr(from, '/');
      if (p == NULL) {
            strncpy(to, from, ep - to);
      } else {
            if (p[1] == '\0') {
                  --p;
                  while (p > from && *p != '/')
                        --p;
            }
            len = 0;
            if (*p == '/') {
                  strncpy(to, "...", ep - to);
                  to += 3;
                  len = 3;
            }
            if (to < ep) {
                  strncpy(to, p, ep - to);
                  len += strlen(to);
            }
      }
      return (len);
}


/*
 * Function to write the path table for the disc.
 */
static int
pathtab_write(FILE *outfile)
{
      /* Next we write the path tables */
      jtwrite(path_table_l, path_blocks << 11, 1, 0, FALSE);
      xfwrite(path_table_l, path_blocks << 11, 1, outfile, 0, FALSE);
      last_extent_written += path_blocks;
      jtwrite(path_table_m, path_blocks << 11, 1, 0, FALSE);
      xfwrite(path_table_m, path_blocks << 11, 1, outfile, 0, FALSE);
      last_extent_written += path_blocks;
      free(path_table_l);
      free(path_table_m);
      path_table_l = NULL;
      path_table_m = NULL;
      return (0);
}

static int
exten_write(FILE *outfile)
{
      jtwrite(extension_record, SECTOR_SIZE, 1, 0, FALSE);
      xfwrite(extension_record, SECTOR_SIZE, 1, outfile, 0, FALSE);
      last_extent_written++;
      return (0);
}

/*
 * Functions to describe padding block at the start of the disc.
 */
int
oneblock_size(int starting_extent)
{
      last_extent++;
      return (0);
}

/*
 * Functions to describe path table size.
 */
static int
pathtab_size(int starting_extent)
{
      path_table[0] = starting_extent;

      path_table[1] = 0;
      path_table[2] = path_table[0] + path_blocks;
      path_table[3] = 0;
      last_extent += 2 * path_blocks;
      return (0);
}

/*
 * Functions to describe padding blocks before PVD.
 */
static int
startpad_size(int starting_extent)
{
      last_extent = session_start + 16;
      return (0);
}

/*
 * Functions to describe padding blocks between sections.
 */
static int
interpad_size(int starting_extent)
{
      int   emod = 0;

#ifdef      needed
      starting_extent += 16;  /* First add 16 pad blocks */
#endif
      if ((emod = starting_extent % 16) != 0) {
            starting_extent += 16 - emod; /* Now pad to mod 16 #     */
      }
      last_extent = starting_extent;
      return (0);
}

/*
 * Functions to describe padding blocks at end of disk.
 */
static int
endpad_size(int starting_extent)
{
      starting_extent += 150;             /* 150 pad blocks (post gap) */
      last_extent = starting_extent;
      return (0);
}

static int
file_gen()
{
#ifdef APPLE_HYB
      int   start_extent = last_extent;   /* orig ISO files start */

#endif      /* APPLE_HYB */

      if (!assign_file_addresses(root, FALSE)) {
#ifdef DVD_VIDEO
            if (dvd_video) {
                  comerrno(EX_BAD, "Unable to make a DVD-Video image.\n"
            "Possible reasons:\n"
            "  - VIDEO_TS subdirectory was not found on specified location\n"
            "  - VIDEO_TS has invalid contents\n"
            );
            }
#else
            ;     /* EMPTY */
#endif
      }


#ifdef SORTING
      if (do_sort) {
            if (sort_file_addresses() == 0)
                  reassign_link_addresses(root);
      }
#endif /* SORTING */

#ifdef APPLE_HYB
      /*
       * put this here for the time being - may when I've worked out how to
       * use Eric's new system for creating/writing parts of the image it
       * may move to it's own routine
       */
      if (apple_hyb)
            hfs_file_gen(start_extent);
#ifdef PREP_BOOT
      else if (use_prep_boot || use_chrp_boot)
            gen_prepboot();
#endif      /* PREP_BOOT */
#endif      /* APPLE_HYB */

      return (0);
}

static int
dirtree_dump()
{
      if (verbose > 2) {
            dump_tree(root);
      }
      return (0);
}

static int
dirtree_fixup(int starting_extent)
{
      if (use_RockRidge && reloc_dir)
            finish_cl_pl_entries();

      if (use_RockRidge)
            update_nlink_field(root);
      return (0);
}

static int
dirtree_size(int starting_extent)
{
      assign_directory_addresses(root);
      return (0);
}

static int
ext_size(int starting_extent)
{
      extern int        extension_record_size;
      struct directory_entry *s_entry;

      extension_record_extent = starting_extent;
      s_entry = root->contents;
      set_733((char *) s_entry->rr_attributes + s_entry->rr_attr_size - 24,
            extension_record_extent);
      set_733((char *) s_entry->rr_attributes + s_entry->rr_attr_size - 8,
            extension_record_size);
      last_extent++;
      return (0);
}

static int
dirtree_write(FILE *outfile)
{
      generate_iso9660_directories(root, outfile);
      return (0);
}

static int
dirtree_cleanup(FILE *outfile)
{
      free_directories(root);
      return (0);
}

static int
startpad_write(FILE *outfile)
{
      char  buffer[SECTOR_SIZE];
      int   i;
      int   npad;

      memset(buffer, 0, sizeof (buffer));

      npad = session_start + 16 - last_extent_written;

      for (i = 0; i < npad; i++) {
            jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
            xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
            last_extent_written++;
      }

      return (0);
}

static int
interpad_write(FILE *outfile)
{
      char  buffer[SECTOR_SIZE];
      int   i;
      int   npad = 0;

      memset(buffer, 0, sizeof (buffer));

#ifdef      needed
      npad = 16;
#endif
      if ((i = last_extent_written % 16) != 0)
            npad += 16 - i;

      for (i = 0; i < npad; i++) {
            jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
            xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
            last_extent_written++;
      }

      return (0);
}

static int
endpad_write(FILE *outfile)
{
      char  buffer[SECTOR_SIZE];
      int   i;

      memset(buffer, 0, sizeof (buffer));

      for (i = 0; i < 150; i++) {
            jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
            xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
            last_extent_written++;
      }

      return (0);
}

#ifdef APPLE_HYB

/*
 *    hfs_get_parms:    get HFS parameters from the command line
 */

static int
hfs_get_parms(char *key)
{
      int   ret = 0;
      char  *p;

      if (hfs_parms == NULL)
            return (ret);

      if ((p = strstr(hfs_parms, key)) != NULL) {
            p += strlen(key) + 1;
            sscanf(p, "%d", &ret);
      }

      return (ret);
}

/*
 *    hfs_file_gen:     set up "fake" HFS volume using the ISO9660 tree
 */
static void
hfs_file_gen(int start_extent)
{
      int   Csize;      /* clump size for HFS vol */
      int   loop;
      int   last_extent_save = last_extent;
      char  *p;

      /* allocate memory for the libhfs/genisoimage extra info */
      hce = (hce_mem *) e_malloc(sizeof (hce_mem));

      hce->error = (char *) e_malloc(1024);

      /* mark as unallocated for use later */
      hce->hfs_ce = hce->hfs_hdr = hce->hfs_map = 0;

      /* reserve space for the label partition - if it is needed */
#ifdef PREP_BOOT
      /* a PReP bootable partition needs the map.. */
      if (gen_pt || use_prep_boot || use_chrp_boot)
#else
      if (gen_pt)
#endif      /* PREP_BOOT */
            hce->hfs_map_size = HFS_MAP_SIZE;
      else
            hce->hfs_map_size = 0;

      /* set the HFS parameter string to upper case */
      if (hfs_parms) {
            for (p = hfs_parms; *p; p++)
                  *p = toupper(*p);
      }

      /* set the initial factor to increase Catalog file size */
      if ((hce->ctc_size = hfs_get_parms("CTC")) == 0)
            hce->ctc_size = CTC;

      /* set the max size of the Catalog file */
      if ((hce->max_XTCsize = hfs_get_parms("MAX_XTCSIZE")) == 0)
            hce->max_XTCsize = MAX_XTCSIZE;

      /* set the number of time to try to make an HFS volume */
      if ((loop = hfs_get_parms("CTC_LOOP")) == 0)
            loop = CTC_LOOP;

      /*
       * "create" the HFS volume (just the header, catalog/extents files) if
       * there's a problem with the Catalog file being too small, we keep on
       * increasing the size (up to CTC_LOOP) times and try again.
       * Unfortunately I don't know enough about the inner workings of HFS,
       * so I can't workout the size of the Catalog file in advance (and I
       * don't want to "grow" as is is normally allowed to), therefore, this
       * approach is a bit over the top as it involves throwing away the
       * "volume" we have created and trying again ...
       */
      do {
            hce->error[0] = '\0';

            /* attempt to create the Mac volume */
            Csize = make_mac_volume(root, start_extent);

            /* if we have a problem ... */
            if (Csize < 0) {
                  /*
                   * we've made too many attempts, or got some other
                   * error
                   */
                  if (loop == 0 || errno != HCE_ERROR) {
                        /* HCE_ERROR is not a valid errno value */
                        if (errno == HCE_ERROR)
                              errno = 0;

                        /* exit with the error */
                        if (*hce->error)
                              fprintf(stderr, "%s\n", hce->error);
                        perr(hfs_error);
                  } else {
                        /* increase Catalog file size factor */
                        hce->ctc_size *= CTC;

                        /*
                         * reset the initial "last_extent" and try
                         * again
                         */
                        last_extent = last_extent_save;
                  }
            } else {
                  /* everything OK - just carry on ... */
                  loop = 0;
            }
      }
      while (loop--);

      hfs_extra = HFS_ROUND_UP(hce->hfs_tot_size) / SECTOR_SIZE;

      last_extent += hfs_extra;

      /* generate the Mac label and HFS partition maps */
      mac_boot.name = hfs_boot_file;

      /*
       * only generate the partition tables etc. if we are making a bootable
       * CD - or if the -part option is given
       */
      if (gen_pt) {
            if (gen_mac_label(&mac_boot)) {
                  if (*hce->error)
                        fprintf(stderr, "%s\n", hce->error);
                  perr(hfs_error);
            }
      }
      /* set Autostart filename if required */
      if (autoname) {
            if (autostart())
                  perr("Autostart filename must less than 12 characters");
      }
      /* finished with any HFS type errors */
      free(hce->error);
      hce->error = 0;

      /*
       * the ISO files need to start on a multiple of the HFS allocation
       * blocks, so find out how much padding we need
       */

      /*
       * take in accout alignment of files wrt HFS volume start - remove any
       * previous session as well
       */
      start_extent -= session_start;
      hfs_pad = ROUND_UP(start_extent*SECTOR_SIZE +
                  (hce->hfs_hdr_size + hce->hfs_map_size) * HFS_BLOCKSZ,
                                          Csize) / SECTOR_SIZE;

      hfs_pad -= (start_extent + (hce->hfs_hdr_size + hce->hfs_map_size) /
                                          HFS_BLK_CONV);

#ifdef PREP_BOOT
      gen_prepboot_label(hce->hfs_map);
#endif      /* PREP_BOOT */

}

#ifdef PREP_BOOT
static void
gen_prepboot()
{
      /*
       * we need to allocate the hce struct since hce->hfs_map is used to
       * generate the fdisk partition map required for PReP booting
       */
      hce = (hce_mem *) e_malloc(sizeof (hce_mem));

      /* mark as unallocated for use later */
      hce->hfs_ce = hce->hfs_hdr = hce->hfs_map = 0;

      /* reserve space for the label partition - if it is needed */
      hce->hfs_map_size = HFS_MAP_SIZE;

      hce->hfs_map = (unsigned char *) e_malloc(hce->hfs_map_size * HFS_BLOCKSZ);
      gen_prepboot_label(hce->hfs_map);
}

#endif      /* PREP_BOOT */

/*
 *    get_adj_size:     get the ajusted size of the volume with the HFS
 *                allocation block size for each file
 */
Ulong
get_adj_size(int Csize)
{
      struct deferred_write *dw;
      Ulong       size = 0;
      int         count = 0;

      /* loop through all the files finding the new total size */
      for (dw = dw_head; dw; dw = dw->next) {
            size += (ROUND_UP(dw->size, Csize)/HFS_BLOCKSZ);
            count++;
      }

      /*
       * crude attempt to prevent overflows - HFS can only cope with a
       * maximum of about 65536 forks (actually less) - this will trap cases
       * when we have far too many files
       */

      if (count >= 65536)
            return (-1);
      else
            return (size);
}

/*
 *    adj_size:   adjust the ISO record entries for all files
 *                based on the HFS allocation block size
 */
int
adj_size(int Csize, int start_extent, int extra)
{
      struct deferred_write *dw;
      struct directory_entry *s_entry;
      int         size;

      /* get the adjusted start_extent (with padding) */
      /* take in accout alignment of files wrt HFS volume start */

      start_extent -= session_start;

      start_extent = ROUND_UP(start_extent*SECTOR_SIZE + extra*HFS_BLOCKSZ,
                                    Csize) / SECTOR_SIZE;

      start_extent -= (extra / HFS_BLK_CONV);

      start_extent += session_start;

      /* initialise file hash */
      flush_hash();

      /*
       * loop through all files changing their starting blocks and finding
       * any padding needed to written out latter
       */
      for (dw = dw_head; dw; dw = dw->next) {
            s_entry = dw->s_entry;
            s_entry->starting_block = dw->extent = start_extent;
            set_733((char *) s_entry->isorec.extent, start_extent);
            size = ROUND_UP(dw->size, Csize) / SECTOR_SIZE;
            dw->pad = size - ISO_ROUND_UP(dw->size) / SECTOR_SIZE;

            /*
             * cache non-HFS files - as there may be multiple links to
             * these files (HFS files can't have multiple links). We will
             * need to change the starting extent of the other links later
             */
            if (!s_entry->hfs_ent)
                  add_hash(s_entry);

            start_extent += size;
      }

      return (start_extent);
}

/*
 *    adj_size_other:   adjust any non-HFS files that may be linked
 *                to an existing file (i.e. not have a deferred_write
 *                entry of it's own
 */
void
adj_size_other(struct directory *dpnt)
{
      struct directory_entry *s_entry;
      struct file_hash *s_hash;

      while (dpnt) {
            s_entry = dpnt->contents;
            for (s_entry = dpnt->contents; s_entry;
                                    s_entry = s_entry->next) {
                  /*
                   * if it's an HFS file or a directory - then ignore
                   * (we're after non-HFS files)
                   */
                  if (s_entry->hfs_ent ||
                      (s_entry->isorec.flags[0] & ISO_DIRECTORY))
                        continue;

                  /*
                   * find any cached entry and assign new starting
                   * extent
                   */
                  s_hash = find_hash(s_entry->dev, s_entry->inode);
                  if (s_hash) {
                        set_733((char *) s_entry->isorec.extent,
                                    s_hash->starting_block);
                        /* not vital - but tidy */
                        s_entry->starting_block =
                                          s_hash->starting_block;
                  }
            }
            if (dpnt->subdir) {
                  adj_size_other(dpnt->subdir);
            }
            dpnt = dpnt->next;
      }

      /* clear file hash */
      flush_hash();
}

/*
 *    hfs_hce_write:    write out the HFS header stuff
 */
static int
hfs_hce_write(FILE *outfile)
{
      char  buffer[SECTOR_SIZE];
      int   n = 0;
      int   r;    /* HFS hdr output */
      int   tot_size = hce->hfs_map_size + hce->hfs_hdr_size;

      memset(buffer, 0, sizeof (buffer));

      /*
       * hack time ... if the tot_size is greater than 32Kb then
       * it won't fit in the first 16 blank SECTORS (64 512 byte
       * blocks, as most of this is padding, we just truncate this
       * data to 64x4xHFS_BLOCKSZ ... hope this is OK ...
       */

      if (tot_size > 64) tot_size = 64;

      /* get size in CD blocks == 4xHFS_BLOCKSZ == 2048 */
      n = tot_size / HFS_BLK_CONV;
      r = tot_size % HFS_BLK_CONV;

      /* write out HFS volume header info */
      jtwrite(hce->hfs_map, HFS_BLOCKSZ, tot_size, 0, FALSE);
      xfwrite(hce->hfs_map, HFS_BLOCKSZ, tot_size, outfile, 0, FALSE);

      /* fill up to a complete CD block */
      if (r) {
            jtwrite(buffer, HFS_BLOCKSZ, HFS_BLK_CONV - r, 0, FALSE);
            xfwrite(buffer, HFS_BLOCKSZ, HFS_BLK_CONV - r, outfile, 0, FALSE);
            n++;
      }
      last_extent_written += n;
      return (0);
}

/*
 *    insert_padding_file : insert a dumy file to make volume at least
 *                      800k
 *
 *    XXX If we ever need to write more then 2 GB, make size off_t
 */
int
insert_padding_file(int size)
{
      struct deferred_write *dwpnt;

      /* get the size in bytes */
      size *= HFS_BLOCKSZ;

      dwpnt = (struct deferred_write *)
            e_malloc(sizeof (struct deferred_write));
      dwpnt->s_entry = 0;
      /* set the padding to zero */
      dwpnt->pad = 0;
      /* set offset to zero */
      dwpnt->off = (off_t)0;

      /*
       * don't need to wory about the s_entry stuff as it won't be touched#
       * at this point onwards
       */

      /* insert the entry in the list */
      if (dw_tail) {
            dw_tail->next = dwpnt;
            dw_tail = dwpnt;
      } else {
            dw_head = dwpnt;
            dw_tail = dwpnt;
      }

      /* aloocate memory as a "Table" file */
      dwpnt->table = e_malloc(size);
      dwpnt->name = NULL;

      dwpnt->next = NULL;
      dwpnt->size = size;
      dwpnt->extent = last_extent;
      last_extent += ISO_BLOCKS(size);

      /* retune the size in HFS blocks */
      return (ISO_ROUND_UP(size) / HFS_BLOCKSZ);
}

struct output_fragment hfs_desc           = {NULL, NULL, NULL, hfs_hce_write, "HFS volume header"};

#endif      /* APPLE_HYB */

struct output_fragment startpad_desc      = {NULL, startpad_size, NULL,   startpad_write, "Initial Padblock"};
struct output_fragment voldesc_desc = {NULL, oneblock_size, root_gen, pvd_write,      "Primary Volume Descriptor"};
struct output_fragment xvoldesc_desc      = {NULL, oneblock_size, NULL,   xpvd_write,       "Enhanced Volume Descriptor"};
struct output_fragment end_vol            = {NULL, oneblock_size, NULL,   evd_write,        "End Volume Descriptor" };
struct output_fragment version_desc = {NULL, oneblock_size, NULL,   vers_write,       "Version block" };
struct output_fragment pathtable_desc     = {NULL, pathtab_size,  generate_path_tables, pathtab_write, "Path table"};
struct output_fragment dirtree_desc = {NULL, dirtree_size,  NULL,   dirtree_write,  "Directory tree" };
struct output_fragment dirtree_clean      = {NULL, dirtree_fixup, dirtree_dump, dirtree_cleanup, "Directory tree cleanup" };
struct output_fragment extension_desc     = {NULL, ext_size,      NULL,   exten_write,      "Extension record" };
struct output_fragment files_desc   = {NULL, NULL,          file_gen, file_write,     "The File(s)"};
struct output_fragment interpad_desc      = {NULL, interpad_size, NULL,   interpad_write, "Intermediate Padblock"};
struct output_fragment endpad_desc  = {NULL, endpad_size,   NULL,   endpad_write,     "Ending Padblock"};

Generated by  Doxygen 1.6.0   Back to index