/*
 * Program mkisofs.c - generate iso9660 filesystem  based upon directory
 * tree on hard disk.

   Written by Eric Youngdale (1993).

   Copyright 1993 Yggdrasil Computing, Incorporated

   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 12/3/99 */

#include <errno.h>
#include "config.h"
#include "mkisofs.h"
#include "match.h"
#include "apple_proto.h"

#include "getopt.h"	/* Always include local (nonstandard) getopt.h */

#include "iso9660.h"
#include <ctype.h>

#ifndef VMS
#include <time.h>
#else
#include <sys/time.h>
#include "vms.h"
#endif

#include <stdlib.h>
#include <sys/stat.h>

#ifndef VMS
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#endif
#include <fctldefs.h>

struct directory * root = NULL;

#ifdef APPLE_HYB
static char version_string[] = "mkhybrid 1.12b5.1";
#else
static char version_string[] = "mkisofs 1.12b5";
#endif /* APPLE_HYB */

char * outfile;
FILE * discimage;
unsigned int next_extent = 0;
unsigned int last_extent = 0;
unsigned int session_start = 0;
unsigned int path_table_size = 0;
unsigned int path_table[4] = {0,};
unsigned int path_blocks = 0;


unsigned int jpath_table_size = 0;
unsigned int jpath_table[4] = {0,};
unsigned int jpath_blocks = 0;

struct iso_directory_record root_record;
struct iso_directory_record jroot_record;

char * extension_record = NULL;
int extension_record_extent = 0;
int extension_record_size = 0;

/* These variables are associated with command line options */
int use_eltorito = 0;
int use_RockRidge = 0;
int use_Joliet = 0;
int verbose = 1;
int all_files  = 0;
int follow_links = 0;
int rationalize = 0;
int generate_tables = 0;
int print_size = 0;
int split_output = 0;
char * preparer = PREPARER_DEFAULT;
char * publisher = PUBLISHER_DEFAULT;
char * appid = APPID_DEFAULT;
char * copyright = COPYRIGHT_DEFAULT;
char * biblio = BIBLIO_DEFAULT;
char * abstract = ABSTRACT_DEFAULT;
char * volset_id = VOLSET_ID_DEFAULT;
char * volume_id = VOLUME_ID_DEFAULT;
char * system_id = SYSTEM_ID_DEFAULT;
char * boot_catalog = BOOT_CATALOG_DEFAULT;
char * boot_image = BOOT_IMAGE_DEFAULT;
int volume_set_size = 1;
int volume_sequence_number = 1;

int use_graft_ptrs = 0;         /* Use graft points */
int jhide_trans_tbl;            /* Hide TRANS.TBL from Joliet tree */
int hide_rr_moved;              /* Name RR_MOVED .rr_moved in Rock Ridge tree */
int omit_period = 0;             /* Violates iso9660, but these are a pain */
int transparent_compression = 0; /* So far only works with linux */
int omit_version_number = 0;     /* May violate iso9660, but noone uses vers*/
int RR_relocation_depth = 6;     /* Violates iso9660, but most systems work */
int full_iso9660_filenames = 0;  /* Used with Amiga.  Disc will not work with
				  DOS */
int allow_leading_dots = 0;	 /* DOS cannot read names with leading dots */
int split_SL_component = 1;    /* circumvent a bug in the SunOS driver */
int split_SL_field = 1;                /* circumvent a bug in the SunOS */

#ifdef APPLE_HYB
int	apple_hyb = 0;		/* create HFS hybrid flag */
int	apple_ext = 0;		/* create HFS extensions flag */
int	apple_both = 0;		/* common flag (for above) */
int	hfs_extra = 0;		/* extra HFS blocks added to end of ISO vol */
int	mac_name = 0;		/* use Mac name for ISO/Joliet/RR flag */
hce_mem *hce;			/* libhfs/mkisofs extras */
char	*hfs_boot_file = 0;	/* name of HFS boot file */
int	gen_pt = 0;		/* generate HFS partition table */
char	*autoname = 0;		/* AutoStart filename */
char	*magic_file = 0;	/* name of magic file */
int	probe = 0;		/* search files for HFS/Unix type */
int	nomacfiles = 0;		/* don't look for Mac/Unix files */
int	hfs_select = 0;		/* Mac/Unix types to select */
int	create_dt = 1;		/* create the Desktp files */
int	afe_size = 0;		/* Apple File Exchange block size */
int	hfs_last = MAG_LAST;	/* process magic file after map file */
char	*deftype = DEFTYPE;	/* default Apple TYPE */
char	*defcreator = DEFCREATOR; /* default Apple CREATOR */
char	*trans_tbl = "TRANS.TBL"; /* default name for translation table */
char	*hfs_volume_id = NULL;	/* HFS volume ID */
char	*hfs_bless = NULL;	/* name of folder to 'bless' (System Folder) */

#endif /* APPLE_HYB */

struct rcopts{
  char * tag;
  char ** variable;
};

struct rcopts rcopt[] = {
  {"PREP", &preparer},
  {"PUBL", &publisher},
  {"APPI", &appid},
  {"COPY", &copyright},
  {"BIBL", &biblio},
  {"ABST", &abstract},
  {"VOLS", &volset_id},
  {"VOLI", &volume_id},
  {"SYSI", &system_id},
#ifdef APPLE_HYB
  {"TYPE", &deftype},
  {"CREATOR", &defcreator},
#endif /* APPLE_HYB */
  {NULL, NULL}
};

/*
 * In case it isn't obvious, the option handling code was ripped off from GNU-ld.
 */
struct ld_option
{
  /* The long option information.  */
  struct option opt;
  /* The short option with the same meaning ('\0' if none).  */
  char shortopt;
  /* The name of the argument (NULL if none).  */
  const char *arg;
  /* The documentation string.  If this is NULL, this is a synonym for
     the previous option.  */
  const char *doc;
  enum
    {
      /* Use one dash before long option name.  */
      ONE_DASH,
      /* Use two dashes before long option name.  */
      TWO_DASHES,
      /* Don't mention this option in --help output.  */
      NO_HELP
    } control;
};

/* Codes used for the long options with no short synonyms.  150 isn't
   special; it's just an arbitrary non-ASCII char value.  */
#define OPTION_HELP			150
#define OPTION_QUIET			151
#define OPTION_NOSPLIT_SL_COMPONENT	152
#define OPTION_NOSPLIT_SL_FIELD		153
#define OPTION_PRINT_SIZE		154
#define OPTION_SPLIT_OUTPUT		155
#define OPTION_ABSTRACT			156
#define OPTION_BIBLIO			157
#define OPTION_COPYRIGHT		158
#define OPTION_SYSID			159
#define OPTION_VOLSET			160
#define OPTION_VOLSET_SIZE		161
#define OPTION_VOLSET_SEQ_NUM		162
#define OPTION_I_HIDE			163
#define OPTION_J_HIDE			164
#define OPTION_LOG_FILE			165
#if 0
#define OPTION_PVERSION			166
#define OPTION_NOBAK			167
#define OPTION_SPARCLABEL		168
#define OPTION_HARD_DISK_BOOT		169
#define OPTION_NO_EMUL_BOOT		170
#define OPTION_NO_BOOT			171
#define OPTION_BOOT_LOAD_ADDR		172
#define OPTION_BOOT_LOAD_SIZE		173
#define OPTION_BOOT_INFO_TABLE		174
#endif
#define OPTION_HIDE_TRANS_TBL		175
#define OPTION_HIDE_RR_MOVED		176
#if 0
#define OPTION_GUI			177
#endif
#define OPTION_TRANS_TBL		178
#define OPTION_P_LIST			179
#define OPTION_I_LIST			180
#define OPTION_J_LIST			181
#define OPTION_X_LIST			182
#if 0
#define OPTION_NO_RR			183
#define OPTION_JCHARSET			184
#define OPTION_PAD			185
#define OPTION_H_HIDE			186
#define OPTION_H_LIST			187
#define OPTION_CHECK_OLDNAMES		188

#ifdef SORTING
#define OPTION_SORT			189
#endif /* SORTING */
#define OPTION_UCS_LEVEL		190
#define OPTION_ISO_TRANSLATE		191
#define OPTION_ISO_LEVEL		192
#define OPTION_RELAXED_FILENAMES	193
#define OPTION_ALLOW_LOWERCASE		194
#define OPTION_ALLOW_MULTIDOT		195
#define OPTION_USE_FILEVERSION		196
#define OPTION_MAX_FILENAMES		197
#define OPTION_ALT_BOOT			198
#endif
#define OPTION_USE_GRAFT		199

#ifdef APPLE_HYB
#define OPTION_CAP			200
#define OPTION_NETA			201
#define OPTION_DBL			202
#define OPTION_ESH			203
#define OPTION_FE			204
#define OPTION_SGI			205
#define OPTION_MBIN			206
#define OPTION_SGL			207
/* aliases */
#define OPTION_USH			208
#define OPTION_XIN			209

#if 0
#define OPTION_DAVE			210
#define OPTION_SFM			211
#endif

#define OPTION_PROBE			220
#define OPTION_MACNAME			221
#define OPTION_NOMACFILES		222
#define OPTION_BOOT_HFS_FILE		223
#define OPTION_MAGIC_FILE		224

#define OPTION_HFS_LIST			225

#define OPTION_GEN_PT			226

#define OPTION_CREATE_DT		227
#define OPTION_HFS_HIDE			228

#define OPTION_AUTOSTART		229
#define OPTION_BSIZE			230
#define OPTION_HFS_VOLID		231

#define OPTION_HFS_BLESS		245
#endif /* APPLE_HYB */

static int	save_pname = 0;

static const struct ld_option ld_options[] =
{
  { {"all-files", no_argument, NULL, 'a'},
      'a', NULL, "Process all files (don't skip backup files)", ONE_DASH },
  { {"abstract", required_argument, NULL, OPTION_ABSTRACT},
      '\0', "FILE", "Set Abstract filename" , ONE_DASH },
  { {"appid", required_argument, NULL, 'A'},
      'A', "ID", "Set Application ID" , ONE_DASH },
  { {"biblio", required_argument, NULL, OPTION_BIBLIO},
      '\0', "FILE", "Set Bibliographic filename" , ONE_DASH },
  { {"copyright", required_argument, NULL, OPTION_COPYRIGHT},
      '\0', "FILE", "Set Copyright filename" , ONE_DASH },
  { {"eltorito-boot", required_argument, NULL, 'b'},
      'b', "FILE", "Set El Torito boot image name" , ONE_DASH },
  { {"eltorito-catalog", required_argument, NULL, 'c'},
      'c', "FILE", "Set El Torito boot catalog name" , ONE_DASH },
  { {"cdwrite-params", required_argument, NULL, 'C'},
      'C', "PARAMS", "Magic paramters from cdrecord" , ONE_DASH },
  { {"omit-period", no_argument, NULL, 'd'},
      'd', NULL, "Omit trailing periods from filenames", ONE_DASH },
  { {"disable-deep-relocation", no_argument, NULL, 'D'},
      'D', NULL, "Disable deep directory relocation", ONE_DASH },
  { {"follow-links", no_argument, NULL, 'f'},
      'f', NULL, "Follow symbolic links", ONE_DASH },
  {{"graft-points", no_argument, NULL, OPTION_USE_GRAFT},
      '\0', NULL, "Allow to use graft points for filenames", ONE_DASH},
  { {"help", no_argument, NULL, OPTION_HELP},
      '\0', NULL, "Print option help", ONE_DASH },
  { {"hide", required_argument, NULL, OPTION_I_HIDE},
      '\0', "GLOBFILE", "Hide ISO9660/RR file" , ONE_DASH },
  { {"hide-list", required_argument, NULL, OPTION_I_LIST},
      '\0', "FILE", "list of ISO9660/RR files to hide" , ONE_DASH },
  { {"hide-joliet", required_argument, NULL, OPTION_J_HIDE},
      '\0', "GLOBFILE", "Hide Joliet file" , ONE_DASH },
  { {"hide-joliet-list", required_argument, NULL, OPTION_J_LIST},
      '\0', "FILE", "List of Joliet files to hide" , ONE_DASH },
  {{"hide-joliet-trans-tbl", no_argument, NULL, OPTION_HIDE_TRANS_TBL},
      '\0', NULL, "Hide TRANS.TBL from Joliet tree", ONE_DASH},
  {{"hide-rr-moved", no_argument, NULL, OPTION_HIDE_RR_MOVED},
      '\0', NULL, "Rename RR_MOVED to .rr_moved in Rock Ridge tree", ONE_DASH},
  { {NULL, required_argument, NULL, 'i'},
      'i', "ADD_FILES", "No longer supported" , TWO_DASHES },
  { {"joliet", no_argument, NULL, 'J'},
      'J', NULL, "Generate Joliet directory information", ONE_DASH },
  { {"full-iso9660-filenames", no_argument, NULL, 'l'},
      'l', NULL, "Allow full 32 character filenames for iso9660 names", ONE_DASH },
  { {"allow-leading-dots", no_argument, NULL, 'L'},
      'L', NULL, "Allow iso9660 filenames to start with '.'", ONE_DASH },
  { {"log-file", required_argument, NULL, OPTION_LOG_FILE},
      '\0', "LOG_FILE", "Re-direct messages to LOG_FILE", ONE_DASH },
  { {"exclude", required_argument, NULL, 'm'},
      'm', "GLOBFILE", "Exclude file name" , ONE_DASH },
  { {"exclude-list", required_argument, NULL, OPTION_X_LIST},
      'm', "FILE", "List of file names to exclude" , ONE_DASH },
  { {"prev-session", required_argument, NULL, 'M'},
      'M', "FILE", "Set path to previous session to merge" , ONE_DASH },
  { {"omit-version-number", no_argument, NULL, 'N'},
      'N', NULL, "Omit version number from iso9660 filename", ONE_DASH },
  { {"no-split-symlink-components", no_argument, NULL, 0},
      0, NULL, "Inhibit splitting symlink components" , ONE_DASH },
  { {"no-split-symlink-fields", no_argument, NULL, 0},
      0, NULL, "Inhibit splitting symlink fields" , ONE_DASH },
  { {"output", required_argument, NULL, 'o'},
      'o', "FILE", "Set output file name" , ONE_DASH },
  { {"path-list", required_argument, NULL, OPTION_P_LIST},
      '\0', "FILE", "list of pathnames to process" , ONE_DASH },
  { {"preparer", required_argument, NULL, 'p'},
      'p', "PREP", "Set Volume preparer" , ONE_DASH },
  { {"print-size", no_argument, NULL, OPTION_PRINT_SIZE},
      '\0', NULL, "Print estimated filesystem size and exit", ONE_DASH },
  { {"publisher", required_argument, NULL, 'P'},
      'P', "PUB", "Set Volume publisher" , ONE_DASH },
  { {"quiet", no_argument, NULL, OPTION_QUIET},
      '\0', NULL, "Run quietly", ONE_DASH },
  { {"rational-rock", no_argument, NULL, 'r'},
      'r', NULL, "Generate rationalized Rock Ridge directory information", ONE_DASH },
  { {"rock", no_argument, NULL, 'R'},
      'R', NULL, "Generate Rock Ridge directory information", ONE_DASH },
  { {"split-output", no_argument, NULL, OPTION_SPLIT_OUTPUT},
      '\0', NULL, "Split output into files of approx. 1GB size", ONE_DASH },
  { {"sysid", required_argument, NULL, OPTION_SYSID},
      '\0', "ID", "Set System ID" , ONE_DASH },
  { {"translation-table", no_argument, NULL, 'T'},
      'T', NULL, "Generate translation tables for systems that don't understand long filenames", ONE_DASH },
  { {"verbose", no_argument, NULL, 'v'},
      'v', NULL, "Verbose", ONE_DASH },
  { {"volid", required_argument, NULL, 'V'},
      'V', "ID", "Set Volume ID" , ONE_DASH },
  { {"volset", required_argument, NULL, OPTION_VOLSET},
      '\0', "ID", "Set Volume set ID" , ONE_DASH },
  { {"volset-size", required_argument, NULL, OPTION_VOLSET_SIZE},
      '\0', "#", "Set Volume set size" , ONE_DASH },
  { {"volset-seqno", required_argument, NULL, OPTION_VOLSET_SEQ_NUM},
      '\0', "#", "Set Volume set sequence number" , ONE_DASH },
  { {"old-exclude", required_argument, NULL, 'x'},
      'x', "FILE", "Exclude file name(depreciated)" , ONE_DASH },
#ifdef ERIC_neverdef
  { {"transparent-compression", no_argument, NULL, 'z'},
      'z', NULL, "Enable transparent compression of files", ONE_DASH },
#endif
#ifdef APPLE_HYB
  { {"apple", no_argument, NULL, 'g'},
      'g', NULL, "Add Apple ISO9660 extensions", ONE_DASH },
  { {"hfs", no_argument, NULL, 'h'},
      'h', NULL, "Create ISO9660/HFS hybrid", ONE_DASH },
  { {"map", required_argument, NULL, 'H'},
      'H', "MAPPING_FILE", "Map file extensions to HFS TYPE/CREATOR", ONE_DASH},
  { {"magic", required_argument, NULL, OPTION_MAGIC_FILE},
      '\0', "FILE", "Magic file for HFS TYPE/CREATOR", ONE_DASH},
  { {"probe", no_argument, NULL, OPTION_PROBE},
      '\0', NULL, "Probe all files for Unix/HFS file type", ONE_DASH },
  { {"mac-name", no_argument, NULL, OPTION_MACNAME},
      '\0', NULL, "Use Macintosh name for ISO9660/Joliet/RockRidge file name",
	ONE_DASH },
  { {"no-mac-files", no_argument, NULL, OPTION_NOMACFILES},
      '\0', NULL, "Do not look for Unix/Mac files", ONE_DASH },
  { {"boot-hfs-file", required_argument, NULL, OPTION_BOOT_HFS_FILE},
      '\0', "FILE", "Set HFS boot image name", ONE_DASH},
  { {"part", no_argument, NULL, OPTION_GEN_PT},
      '\0', NULL, "Generate HFS partition table", ONE_DASH },
  { {"cluster-size", required_argument, NULL, OPTION_BSIZE},
      '\0', "SIZE", "Cluster size for PC Exchange Macintosh files", ONE_DASH},
  { {"auto", required_argument, NULL, OPTION_AUTOSTART},
      '\0', "FILE", "Set HFS AutoStart file name", ONE_DASH},
  { {"no-desktop", no_argument, NULL, OPTION_CREATE_DT},
      '\0', NULL, "Do not create the HFS (empty) Desktop files", ONE_DASH },
  { {"hide-hfs", required_argument, NULL, OPTION_HFS_HIDE},
      '\0', "GLOBFILE", "Hide HFS file" , ONE_DASH },
  { {"hide-hfs-list", required_argument, NULL, OPTION_HFS_LIST},
      '\0', "GLOBFILE", "List of HFS files to hide" , ONE_DASH },
  { {"table-name", required_argument, NULL, OPTION_TRANS_TBL},
      '\0', "TABLE_NAME", "translation table file name", ONE_DASH },
  { {"hfs-volid", required_argument, NULL, OPTION_HFS_VOLID},
      '\0', "HFS_VOLID", "Volume name for the HFS partition", ONE_DASH },
  {{"hfs-bless", required_argument, NULL, OPTION_HFS_BLESS},
      '\0', "FOLDER_NAME", "Name of Folder to be blessed", ONE_DASH},
  { {"cap", no_argument, NULL, OPTION_CAP},
      '\0', NULL, "Look for AUFS CAP Macintosh files", TWO_DASHES },
  { {"netatalk", no_argument, NULL, OPTION_NETA},
      '\0', NULL, "Look for NETATALK Macintosh files", TWO_DASHES },
  { {"double", no_argument, NULL, OPTION_DBL},
      '\0', NULL, "Look for AppleDouble Macintosh files", TWO_DASHES },
  { {"ethershare", no_argument, NULL, OPTION_ESH},
      '\0', NULL, "Look for Helios EtherShare Macintosh files", TWO_DASHES },
  { {"exchange", no_argument, NULL, OPTION_FE},
      '\0', NULL, "Look for PC Exchange Macintosh files", TWO_DASHES },
  { {"sgi", no_argument, NULL, OPTION_SGI},
      '\0', NULL, "Look for SGI Macintosh files", TWO_DASHES },
  { {"macbin", no_argument, NULL, OPTION_MBIN},
      '\0', NULL, "Look for MacBinary Macintosh files", TWO_DASHES },
  { {"single", no_argument, NULL, OPTION_SGL},
      '\0', NULL, "Look for AppleSingle Macintosh files", TWO_DASHES },
  { {"ushare", no_argument, NULL, OPTION_USH},
      '\0', NULL, "Look for IPT UShare Macintosh files", TWO_DASHES },
  { {"xinet", no_argument, NULL, OPTION_XIN},
      '\0', NULL, "Look for XINET Macintosh files", TWO_DASHES },
#endif /* APPLE_HYB */
};

#define OPTION_COUNT (sizeof ld_options / sizeof ld_options[0])

#if defined(ultrix) || defined(_AUX_SOURCE)
char *strdup(s)
char *s;{char *c;if(c=(char *)malloc(strlen(s)+1))strcpy(c,s);return c;}
#endif

	void read_rcfile	__PR((char * appname));
	void usage		__PR((void));
static	void hide_reloc_dir	__PR((void));
static  char * get_pnames	__PR((int argc, char **argv, int opt,
					char *pname, int pnsize, FILE * fp));

	int main		__PR((int argc, char **argv));
	char * findequal	__PR((char *s));
	char * escstrcpy	__PR((char *to, char *from));

void FDECL1(read_rcfile, char *, appname)
{
  FILE * rcfile;
  struct rcopts * rco;
  char * pnt, *pnt1;
  char linebuffer[256];
  static char rcfn[] = ".mkisofsrc";
  char filename[1000];
  int linum;

  strcpy(filename, rcfn);
  rcfile = fopen(filename, "r");
  if (!rcfile && errno != ENOENT)
    perror(filename);

  if (!rcfile)
    {
      pnt = getenv("MKISOFSRC");
      if (pnt && strlen(pnt) <= sizeof(filename))
	{
	  strcpy(filename, pnt);
	  rcfile = fopen(filename, "r");
	  if (!rcfile && errno != ENOENT)
	    perror(filename);
	}
    }

  if (!rcfile)
    {
      pnt = getenv("HOME");
      if (pnt && strlen(pnt) + strlen(rcfn) + 2 <= sizeof(filename))
	{
	  strcpy(filename, pnt);
	  strcat(filename, "/");
	  strcat(filename, rcfn);
	  rcfile = fopen(filename, "r");
	  if (!rcfile && errno != ENOENT)
	    perror(filename);
	}
    }
  if (!rcfile && strlen(appname)+sizeof(rcfn)+2 <= sizeof(filename))
    {
      strcpy(filename, appname);
      pnt = strrchr(filename, '/');
      if (pnt)
	{
	  strcpy(pnt + 1, rcfn);
	  rcfile = fopen(filename, "r");
	  if (!rcfile && errno != ENOENT)
	    perror(filename);
	}
    }
  if (!rcfile)
    return;
  if ( verbose > 0 )
    {
      fprintf(stderr, "Using \"%s\"\n", filename);
    }

  /* OK, we got it.  Now read in the lines and parse them */
  linum = 0;
  while (fgets(linebuffer, sizeof(linebuffer), rcfile))
    {
      char *name;
      char *name_end;
      ++linum;
      /* skip any leading white space */
	pnt = linebuffer;
      while (*pnt == ' ' || *pnt == '\t')
	++pnt;
      /* If we are looking at a # character, this line is a comment. */
	if (*pnt == '#')
	  continue;
      /* The name should begin in the left margin.  Make sure it is in
	 upper case.  Stop when we see white space or a comment. */
	name = pnt;
      while (*pnt && isalpha((unsigned char)*pnt))
	{
	  if(islower((unsigned char)*pnt))
	    *pnt = toupper((unsigned char)*pnt);
	  pnt++;
	}
      if (name == pnt)
	{
	  fprintf(stderr, "%s:%d: name required\n", filename, linum);
	  continue;
	}
      name_end = pnt;
      /* Skip past white space after the name */
      while (*pnt == ' ' || *pnt == '\t')
	pnt++;
      /* silently ignore errors in the rc file. */
      if (*pnt != '=')
	{
	  fprintf(stderr, "%s:%d: equals sign required\n", filename, linum);
	  continue;
	}
      /* Skip pas the = sign, and any white space following it */
      pnt++; /* Skip past '=' sign */
      while (*pnt == ' ' || *pnt == '\t')
	pnt++;

      /* now it is safe to NUL terminate the name */

	*name_end = 0;

      /* Now get rid of trailing newline */

      pnt1 = pnt;
      while (*pnt1)
	{
	  if (*pnt1 == '\n')
	    {
	      *pnt1 = 0;
	      break;
	    }
	  pnt1++;
	};
      /* OK, now figure out which option we have */
      for(rco = rcopt; rco->tag; rco++) {
	if(strcmp(rco->tag, name) == 0)
	  {
	    *rco->variable = strdup(pnt);
	    break;
	  };
      }
      if (rco->tag == NULL)
	{
	  fprintf(stderr, "%s:%d: field name \"%s\" unknown\n", filename, linum,
		  name);
	}
     }
  if (ferror(rcfile))
    perror(filename);
  fclose(rcfile);
}

char * path_table_l = NULL;
char * path_table_m = NULL;

char * jpath_table_l = NULL;
char * jpath_table_m = NULL;

int goof = 0;

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

void usage(){
#ifdef APPLE_HYB
  const char * program_name = "mkhybrid";
#else
  const char * program_name = "mkisofs";
#endif /* APPLE_HYB */

#if 0
	fprintf(stderr,"Usage:\n");
	fprintf(stderr,
"mkisofs [-o outfile] [-R] [-V volid] [-v] [-a] \
[-T]\n [-l] [-d] [-V] [-D] [-L] [-p preparer]"
"[-P publisher] [ -A app_id ] [-z] \n \
[-b boot_image_name] [-c boot_catalog-name] \
[-x path -x path ...] path\n");
#endif

  int i;
/*  const char **targets, **pp;*/

  fprintf (stderr, "Usage: %s [options] file...\n", program_name);

  fprintf (stderr, "Options:\n");
  for (i = 0; i < OPTION_COUNT; i++)
    {
      if (ld_options[i].doc != NULL)
	{
	  int comma;
	  int len;
	  int j;

	  fprintf (stderr, "  ");

	  comma = FALSE;
	  len = 2;

	  j = i;
	  do
	    {
	      if (ld_options[j].shortopt != '\0'
		  && ld_options[j].control != NO_HELP)
		{
		  fprintf (stderr, "%s-%c", comma ? ", " : "", ld_options[j].shortopt);
		  len += (comma ? 2 : 0) + 2;
		  if (ld_options[j].arg != NULL)
		    {
		      if (ld_options[j].opt.has_arg != optional_argument)
			{
			  fprintf (stderr, " ");
			  ++len;
			}
		      fprintf (stderr, "%s", ld_options[j].arg);
		      len += strlen (ld_options[j].arg);
		    }
		  comma = TRUE;
		}
	      ++j;
	    }
	  while (j < OPTION_COUNT && ld_options[j].doc == NULL);

	  j = i;
	  do
	    {
	      if (ld_options[j].opt.name != NULL
		  && ld_options[j].control != NO_HELP)
		{
		  fprintf (stderr, "%s-%s%s",
			  comma ? ", " : "",
			  ld_options[j].control == TWO_DASHES ? "-" : "",
			  ld_options[j].opt.name);
		  len += ((comma ? 2 : 0)
			  + 1
			  + (ld_options[j].control == TWO_DASHES ? 1 : 0)
			  + strlen (ld_options[j].opt.name));
		  if (ld_options[j].arg != NULL)
		    {
		      fprintf (stderr, " %s", ld_options[j].arg);
		      len += 1 + strlen (ld_options[j].arg);
		    }
		  comma = TRUE;
		}
	      ++j;
	    }
	  while (j < OPTION_COUNT && ld_options[j].doc == NULL);

	  if (len >= 30)
	    {
	      fprintf (stderr, "\n");
	      len = 0;
	    }

	  for (; len < 30; len++)
	    fputc (' ', stderr);

	  fprintf (stderr, "%s\n", ld_options[i].doc);
	}
    }
  exit(1);
}


/* 
 * Fill in date in the iso9660 format 
 *
 * The standards  state that the timezone offset is in multiples of 15
 * minutes, and is what you add to GMT to get the localtime.  The U.S.
 * is always at a negative offset, from -5h to -8h (can vary a little
 * with DST,  I guess).  The Linux iso9660 filesystem has had the sign
 * of this wrong for ages (mkisofs had it wrong too for the longest time).
 */
int FDECL2(iso9660_date,char *, result, time_t, crtime){
  struct tm *local;
  local = localtime(&crtime);
  result[0] = local->tm_year;
  result[1] = local->tm_mon + 1;
  result[2] = local->tm_mday;
  result[3] = local->tm_hour;
  result[4] = local->tm_min;
  result[5] = local->tm_sec;

  /* 
   * Must recalculate proper timezone offset each time,
   * as some files use daylight savings time and some don't... 
   */
  result[6] = local->tm_yday;	/* save yday 'cause gmtime zaps it */
  local = gmtime(&crtime);
  local->tm_year -= result[0];
  local->tm_yday -= result[6];
  local->tm_hour -= result[3];
  local->tm_min -= result[4];
  if (local->tm_year < 0) 
    {
      local->tm_yday = -1;
    }
  else 
    {
      if (local->tm_year > 0) local->tm_yday = 1;
    }

  result[6] = -(local->tm_min + 60*(local->tm_hour + 24*local->tm_yday)) / 15;

  return 0;
}

/* hide "./rr_moved" if all its contents are hidden */
static void
hide_reloc_dir()
{
	struct directory_entry * s_entry;

	for (s_entry = reloc_dir->contents; s_entry; s_entry = s_entry->next) {
	    if(strcmp(s_entry->name,".")==0 || strcmp(s_entry->name,"..")==0)
		continue;

	    if((s_entry->de_flags & INHIBIT_ISO9660_ENTRY) == 0)
		return;
	}

	/* all entries are hidden, so hide this directory */
	reloc_dir->dir_flags |= INHIBIT_ISO9660_ENTRY;
	reloc_dir->self->de_flags |= INHIBIT_ISO9660_ENTRY;
}

/* get pathnames from the command line, and then from given file */
static char *
get_pnames(int argc, char **argv, int opt, char *pname, int pnsize, FILE * fp)
{
	int len;

	/* we may of already read the first line from the pathnames file */
	if (save_pname) {
		save_pname = 0;
		return (pname);
	}

	if (opt < argc)
	    return (argv[opt]);

	if (fp == NULL)
	    return ((char *)0);

	if (fgets(pname, pnsize, fp)) {
		/* Discard newline */
		len = strlen(pname);
		if (pname[len - 1] == '\n') {
			pname[len - 1] = '\0';
		}
		return (pname);
	}

	return ((char *)0);
}

extern char * cdwrite_data;

int FDECL2(main, int, argc, char **, argv){
  struct directory_entry de;
#ifdef HAVE_SBRK
  unsigned long mem_start;
#endif
  struct stat statbuf;
  char * merge_image = NULL;
  struct iso_directory_record * mrootp = NULL;
  struct output_fragment * opnt;
  int longind;
  char shortopts[OPTION_COUNT * 3 + 2];
  struct option longopts[OPTION_COUNT + 1];
  int c;
  char *log_file = 0;
  char *node = NULL;
  char *pathnames = 0;
  FILE *pfp = NULL;
  char pname[2*PATH_MAX + 1 + 1];	/* may be too short */
  char *arg;				/* if '\\' present */
  char nodename[PATH_MAX + 1];
  int no_path_names = 1;
  int have_cmd_line_pathspec = 0;
#ifdef APPLE_HYB
  char *afpfile = "";		/* mapping file for TYPE/CREATOR */
#endif /* APPLE_HYB */

  if (argc < 2)
    usage();

  /* Get the defaults from the .mkisofsrc file */
  read_rcfile(argv[0]);

  outfile = NULL;

  /*
   * Copy long option initialization from GNU-ld.
   */
  /* Starting the short option string with '-' is for programs that
     expect options and other ARGV-elements in any order and that care about
     the ordering of the two.  We describe each non-option ARGV-element
     as if it were the argument of an option with character code 1.  */
  {
    int i, is, il;
    shortopts[0] = '-';
    is = 1;
    il = 0;
    for (i = 0; i < OPTION_COUNT; i++)
      {
	if (ld_options[i].shortopt != '\0')
	  {
	    shortopts[is] = ld_options[i].shortopt;
	    ++is;
	    if (ld_options[i].opt.has_arg == required_argument
		|| ld_options[i].opt.has_arg == optional_argument)
	      {
		shortopts[is] = ':';
		++is;
		if (ld_options[i].opt.has_arg == optional_argument)
		  {
		    shortopts[is] = ':';
		    ++is;
		  }
	      }
	  }
	if (ld_options[i].opt.name != NULL)
	  {
	    longopts[il] = ld_options[i].opt;
	    ++il;
	  }
      }
    shortopts[is] = '\0';
    longopts[il].name = NULL;
  }

  while ((c = getopt_long_only (argc, argv, shortopts, longopts, &longind)) != EOF)
    switch (c)
      {
      case 1:
	/*
	 * A filename that we take as input.
	 */
	optind--;
	have_cmd_line_pathspec = 1;
	goto parse_input_files;

      case OPTION_USE_GRAFT:
	use_graft_ptrs = 1;
	break;
      case 'C':
	/*
	 * This is a temporary hack until cdwrite gets the proper hooks in
	 * it.
	 */
	cdwrite_data = optarg;
	break;
      case 'i':
	fprintf(stderr, "-i option no longer supported.\n");
	exit(1);
	break;
      case 'J':
	use_Joliet++;
	break;
      case 'a':
	all_files++;
	break;
      case 'b':
	use_eltorito++;
	boot_image = optarg;  /* pathname of the boot image on cd */
	if (boot_image == NULL) {
	        fprintf(stderr,"Required boot image pathname missing\n");
		exit(1);
	}
	break;
      case 'c':
	use_eltorito++;
	boot_catalog = optarg;  /* pathname of the boot image on cd */
	if (boot_catalog == NULL) {
	        fprintf(stderr,"Required boot catalog pathname missing\n");
		exit(1);
	}
	break;
      case OPTION_ABSTRACT:
	abstract = optarg;
	if(strlen(abstract) > 37) {
		fprintf(stderr,"Abstract filename string too long\n");
		exit(1);
	};
	break;
      case 'A':
	appid = optarg;
	if(strlen(appid) > 128) {
		fprintf(stderr,"Application-id string too long\n");
		exit(1);
	};
	break;
      case OPTION_BIBLIO:
	biblio = optarg;
	if(strlen(biblio) > 37) {
		fprintf(stderr,"Bibliographic filename string too long\n");
		exit(1);
	};
	break;
      case OPTION_COPYRIGHT:
	copyright = optarg;
	if(strlen(copyright) > 37) {
		fprintf(stderr,"Copyright filename string too long\n");
		exit(1);
	};
	break;
      case 'd':
	omit_period++;
	break;
      case 'D':
	RR_relocation_depth = 32767;
	break;
      case 'f':
	follow_links++;
	break;
      case 'l':
	full_iso9660_filenames++;
	break;
      case 'L':
        allow_leading_dots++;
        break;
     case OPTION_LOG_FILE:
	log_file = optarg;
	break;
      case 'M':
	merge_image = optarg;
	break;
      case 'N':
	omit_version_number++;
	break;
      case 'o':
	outfile = optarg;
	break;
      case 'p':
	preparer = optarg;
	if(strlen(preparer) > 128) {
		fprintf(stderr,"Preparer string too long\n");
		exit(1);
	};
	break;
      case OPTION_PRINT_SIZE:
	print_size++;
	break;
      case 'P':
	publisher = optarg;
	if(strlen(publisher) > 128) {
		fprintf(stderr,"Publisher string too long\n");
		exit(1);
	};
	break;
      case OPTION_QUIET:
	verbose = 0;
	break;
      case 'R':
	use_RockRidge++;
	break;
      case 'r':
	rationalize++;
	use_RockRidge++;
	break;
      case OPTION_SPLIT_OUTPUT:
	split_output++;
	break;
      case OPTION_SYSID:
	system_id = optarg;
	if(strlen(system_id) > 32) {
		fprintf(stderr,"System ID string too long\n");
		exit(1);
	};
	break;
#ifdef APPLE_HYB
      case OPTION_TRANS_TBL:
	trans_tbl = optarg;
	/* fall through */
#endif /* APPLE_HYB */
      case 'T':
	generate_tables++;
	break;
      case 'V':
	volume_id = optarg;
	if(strlen(volume_id) > 32) {
		fprintf(stderr,"Volume ID string too long\n");
		exit(1);
	};
	break;
      case OPTION_VOLSET:
	volset_id = optarg;
	if(strlen(volset_id) > 128) {
		fprintf(stderr,"Volume set ID string too long\n");
		exit(1);
	};
	break;
      case OPTION_VOLSET_SIZE:
	volume_set_size = atoi(optarg);
	break;
      case OPTION_VOLSET_SEQ_NUM:
	volume_sequence_number = atoi(optarg);
	if (volume_sequence_number > volume_set_size) {
		fprintf(stderr,"Volume set sequence number too big\n");
		exit(1);
	}
	break;
      case 'v':
	verbose++;
	break;
      case 'z':
#ifdef VMS
	fprintf(stderr,"Transparent compression not supported with VMS\n");
	exit(1);
#else
	transparent_compression++;
#endif
	break;
      case 'x':
      case 'm':
	/*
	 * Somehow two options to do basically the same thing got added somewhere along
	 * the way.  The 'match' code supports limited globbing, so this is the one
	 * that got selected.  Unfortunately the 'x' switch is probably more intuitive.
	 */
        add_match(optarg);
	break;
      case OPTION_I_HIDE:
	i_add_match(optarg);
	break;
      case OPTION_J_HIDE:
	j_add_match(optarg);
	break;
      case OPTION_HIDE_TRANS_TBL:
	jhide_trans_tbl++;
	break;
      case OPTION_HIDE_RR_MOVED:
	hide_rr_moved++;
	break;
      case OPTION_HELP:
	usage ();
	exit (0);
	break;
      case OPTION_NOSPLIT_SL_COMPONENT:
	split_SL_component = 0;
	break;
      case OPTION_NOSPLIT_SL_FIELD:
	split_SL_field = 0;
	break;
#ifdef APPLE_HYB
      case 'H':
	afpfile = optarg;
	hfs_last = MAP_LAST;
	break;
      case 'h':
	apple_hyb = 1;
	break;
      case 'g':
	apple_ext = 1;
	break;
      case OPTION_PROBE:
	probe = 1;
	break;
      case OPTION_MACNAME:
	mac_name = 1;
	break;
      case OPTION_NOMACFILES:
	nomacfiles = 1;
	break;
      case OPTION_BOOT_HFS_FILE:
	hfs_boot_file = optarg;
	/* fall through */
      case OPTION_GEN_PT:
	gen_pt = 1;
	break;
      case OPTION_MAGIC_FILE:
	magic_file = optarg;
	hfs_last = MAG_LAST;
	break;
      case OPTION_AUTOSTART:
	autoname = optarg;
	/* gen_pt = 1; */
	break;
      case OPTION_BSIZE:
	afe_size = atoi(optarg);
	hfs_select |= DO_FEU;
	hfs_select |= DO_FEL;
	break;
      case OPTION_HFS_VOLID:
	hfs_volume_id = optarg;
	break;
      case OPTION_HFS_BLESS:
	hfs_bless = optarg;
	break;
      /* Mac/Unix types to include */
      case OPTION_CAP:
	hfs_select |= DO_CAP;
	break;
      case OPTION_NETA:
	hfs_select |= DO_NETA;
	break;
      case OPTION_DBL:
	hfs_select |= DO_DBL;
	break;
      case OPTION_ESH:
      case OPTION_USH:
	hfs_select |= DO_ESH;
	break;
      case OPTION_FE:
	hfs_select |= DO_FEU;
	hfs_select |= DO_FEL;
	break;
      case OPTION_SGI:
      case OPTION_XIN:
	hfs_select |= DO_SGI;
	break;
      case OPTION_MBIN:
	hfs_select |= DO_MBIN;
	break;
      case OPTION_SGL:
	hfs_select |= DO_SGL;
	break;
      case OPTION_CREATE_DT:
	create_dt = 0;
	break;
      case OPTION_HFS_HIDE:
        hfs_add_match(optarg);
	break;
      case OPTION_HFS_LIST:
	hfs_add_list(optarg);
	break;
#endif /* APPLE_HYB */
      case OPTION_P_LIST:
	pathnames = optarg;
	break;
      case OPTION_X_LIST:
	add_list(optarg);
	break;
      case OPTION_I_LIST:
	i_add_list(optarg);
	break;
      case OPTION_J_LIST:
	j_add_list(optarg);
	break;
      default:
	usage();
	exit(1);
      }

parse_input_files:

#ifdef HAVE_SBRK
  mem_start = (unsigned long) sbrk(0);
#endif

  /* if the -hide-joliet option has been given, set the Joliet option */
  if (!use_Joliet && j_ishidden())
    use_Joliet++;

#ifdef APPLE_HYB
  if (apple_hyb && apple_ext) {
    fprintf(stderr,"can't have both -apple and -hfs options");
    exit (1);
  }

  /* if -probe, -macname, any hfs selection and/or mapping file is given,
     but no HFS option, then select apple_hyb */
  if (!apple_hyb && !apple_ext) {
    if (*afpfile || probe || mac_name || nomacfiles || hfs_select || hfs_boot_file || magic_file || hfs_ishidden() || gen_pt || autoname || afe_size)
	apple_hyb = 1;
  }

  if (apple_ext && hfs_boot_file) {
    fprintf(stderr,"can't have -hfs-boot-file with -apple\n");
    exit (1);
  }

  if (hfs_select)
    /* if we have selected certain types of Mac/Unix files, then turn off
       probe and nomacfiles */
    probe = nomacfiles = 0;

  if (apple_hyb || apple_ext)
    apple_both = 1;

  if (apple_both && verbose && !afe_size &&
      (hfs_select & (DO_FEU | DO_FEL))) {
    fprintf(stderr,
    "Warning: assuming PC Exchange cluster size of 512 bytes\n");
    afe_size = 512;
  }
  if (apple_both) {
    /* set up the TYPE/CREATOR mappings */
    hfs_init(afpfile, 0, probe, nomacfiles, hfs_select);
  }

  if (apple_ext && !use_RockRidge) {
    /* use RockRidge to set the SystemUse field ... */
    use_RockRidge++;
    rationalize++;
  }

#endif /* APPLE_HYB */

  if(verbose > 1) fprintf(stderr,"%s\n", version_string);

  if(cdwrite_data == NULL && merge_image != NULL)
    {
      fprintf(stderr,"Multisession usage bug: Must specify -C if -M is used.\n");
      exit(0);
    }

  if(cdwrite_data != NULL && merge_image == NULL)
    {
      fprintf(stderr,"Warning: -C specified without -M: old session data will not be merged.\n");
    }

  /*
   * see if we have a list of pathnames to process
   */
  if (pathnames) {
    /* "-" means take list from the standard input */
    if (strcmp(pathnames, "-")) {
      if ((pfp = fopen(pathnames, "r")) == NULL) {
        fprintf(stderr,
                "Unable to open pathname list %s.\n", pathnames);
	exit(1);
      }
    } else
      pfp = stdin;
  }

  /*  The first step is to scan the directory tree, and take some notes */

  if ((arg = get_pnames(argc, argv, optind, pname,
			sizeof(pname), pfp)) == NULL) {
	  usage();
	  exit(1);
  };

  /*
   * if we don't have a pathspec, then save the pathspec found
   * in the pathnames file (stored in pname) - we don't want
   * to skip this pathspec when we read the pathnames file again
   */
  if (!have_cmd_line_pathspec) {
    save_pname = 1;
  }

  if(use_RockRidge){
#if 1
	extension_record = generate_rr_extension_record("RRIP_1991A",
				       "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS",
				       "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE.  SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR CONTACT INFORMATION.", &extension_record_size);
#else
	extension_record = generate_rr_extension_record("IEEE_P1282",
				       "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS",
				       "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION.", &extension_record_size);
#endif
  }

  if (log_file) {
    FILE *lfp;
    int i;

    /* open log file - test that we can open OK */
    if ((lfp = fopen(log_file, "w")) == NULL) {
      fprintf(stderr,"can't open logfile: %s\n", log_file);
      exit (1);
    }
    fclose(lfp);

    /* redirect all stderr message to log_file */
    fprintf(stderr, "re-directing all messages to %s\n", log_file);
    fflush(stderr);

    /* associate stderr with the log file */
    if (freopen(log_file, "w", stderr) == NULL) {
      fprintf(stderr,"can't open logfile: %s\n", log_file);
      exit (1);
    }
    if(verbose > 1) {
      for (i=0;i<argc;i++)
       fprintf(stderr,"%s ", argv[i]);

      fprintf(stderr,"\n%s\n", version_string);
    }
  }
  /* Find name of root directory. */
  if (arg != NULL)
    node = findequal(arg);
  if (!use_graft_ptrs)
    node = NULL;
  if (node == NULL) {
    if (use_graft_ptrs && arg != NULL)
      node = escstrcpy(nodename, arg);
    else
      node = arg;
  } else {
    /*
     * Remove '\\' escape chars which are located
     * before '\\' and '=' chars
     */
    node = escstrcpy(nodename, ++node);
  }
  /*
   * See if boot catalog file exists in root directory, if not
   * we will create it.
   */
  if (use_eltorito)
    init_boot_catalog(argv[optind]);

  /*
   * Find the device and inode number of the root directory.
   * Record this in the hash table so we don't scan it more than
   * once.
   */
  stat_filter(argv[optind], &statbuf);
  add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf));

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

  de.filedir = root;  /* We need this to bootstrap */

  if (cdwrite_data != NULL && merge_image == NULL) {
    /* in case we want to add a new session, but don't want to merge old one */
    get_session_start(NULL);
  }

  if( merge_image != NULL )
    {
      mrootp = merge_isofs(merge_image);
      if( mrootp == NULL )
	{
	  /*
	   * Complain and die.
	   */
	  fprintf(stderr,"Unable to open previous session image %s\n",
		  merge_image);
	  exit(1);
	}

      memcpy(&de.isorec.extent, mrootp->extent, 8);      
    }

  /*
   * Create an empty root directory. If we ever scan it for real, we will fill in the
   * contents.
   */
  find_or_create_directory(NULL, "", &de, TRUE);

  /*
   * Scan the actual directory (and any we find below it)
   * for files to write out to the output image.  Note - we
   * take multiple source directories and keep merging them
   * onto the image.
   */
  while((arg = get_pnames(argc, argv, optind, pname,
					sizeof(pname), pfp)) != NULL)
    {
      struct directory * graft_dir;
      struct stat        st;
      char             * short_name;
      int                status;
      char   graft_point[PATH_MAX + 1];

      /*
       * We would like a syntax like:
       *
       * /tmp=/usr/tmp/xxx
       *
       * where the user can specify a place to graft each
       * component of the tree.  To do this, we may have to create
       * directories along the way, of course.
       * Secondly, I would like to allow the user to do something
       * like:
       *
       * /home/baz/RMAIL=/u3/users/baz/RMAIL
       *
       * so that normal files could also be injected into the tree
       * at an arbitrary point.
       *
       * The idea is that the last component of whatever is being
       * entered would take the name from the last component of
       * whatever the user specifies.
       *
       * The default will be that the file is injected at the
       * root of the image tree.
       */
      node = findequal(arg);
      if (!use_graft_ptrs)
        node = NULL;
      /*
       * Remove '\\' escape chars which are located
       * before '\\' and '=' chars ---> below in escstrcpy()
       */

      short_name = NULL;

      if( node != NULL )
	{
	  char * pnt;
	  char * xpnt;
	  size_t len;

	  if (node) {
	    *node = '\0';
	    escstrcpy(graft_point, arg);
	    *node = '=';
	  }

	  /*
	   * Remove unwanted "./" & "/" sequences from start...
	   */
	  do {
	    xpnt = graft_point;
	    while (xpnt[0] == '.' && xpnt[1] == '/')
	      xpnt += 2;
	    while (*xpnt == PATH_SEPARATOR) {
	      xpnt++;
	    }
	    strcpy(graft_point, xpnt);
	  } while (xpnt > graft_point);

	  if (node) {
	    node = escstrcpy(nodename, ++node);
	  } else {
	    node = arg;
	  }

	  graft_dir = root;
	  xpnt = graft_point;
	  /*
	   * If "node" points to a directory, then graft_point
	   * needs to point to a directory too.
	   */
	  if (follow_links)
	    status = stat_filter(node, &st);
	  else
	    status = lstat_filter(node, &st);
	  if (status == 0 && S_ISDIR(st.st_mode)) {
	    len = strlen(graft_point);

	    if ((len <= (sizeof (graft_point) -1)) &&
		graft_point[len-1] != '/') {
	      graft_point[len++] = '/';
	      graft_point[len] = '\0';
	    }
	  }

	  /*
	   * Loop down deeper and deeper until we
	   * find the correct insertion spot.
	   * Canonicalize the filename while parsing it.
	   */
	  while(1==1)
	    {
	      do {
		while (xpnt[0] == '.' && xpnt[1] == '/')
		  xpnt += 2;
		while (xpnt[0] == '/')
		  xpnt += 1;
		if (xpnt[0] == '.' && xpnt[1] == '.' && xpnt[2] == '/') {
		  if (graft_dir && graft_dir != root) {
		    graft_dir = graft_dir->parent;
		    xpnt += 2;
		  }
	        }
	      } while ((xpnt[0] == '/') || (xpnt[0] == '.' && xpnt[1] == '/'));
	      pnt = strchr(xpnt, PATH_SEPARATOR);
	      if( pnt == NULL )
		{
		  if( *xpnt != '\0' )
		    {
		      short_name = xpnt;
		    }
		  break;
		}
	      *pnt = '\0';
	      graft_dir = find_or_create_directory(graft_dir, 
						   graft_point, 
						   NULL, TRUE);
	      *pnt = PATH_SEPARATOR;
	      xpnt = pnt + 1;
	    }
	}
      else
	{
	  graft_dir = root;
	  if (use_graft_ptrs)
	    node = escstrcpy(nodename, arg);
	  else
	    node = arg;
	}

      /*
       * Now see whether the user wants to add a regular file,
       * or a directory at this point.
       */
      if (follow_links)
	status = stat_filter(node, &st);
      else
	status = lstat_filter(node, &st);
      if( status != 0 )
	{
	  /*
	   * This is a fatal error - the user won't be getting what
	   * they want if we were to proceed.
	   */
	  fprintf(stderr, "Invalid node - %s\n", node);
	  exit(1);
	}
      else
	{
	  if( S_ISDIR(st.st_mode) )
	    {
	      if (!scan_directory_tree(graft_dir, node, &de))
		{
		  exit(1);
		}
	    }
	  else
	    {
	      if( short_name == NULL )
		{
		  short_name = strrchr(node, PATH_SEPARATOR);
		  if( short_name == NULL || short_name < node )
		    {
		      short_name = node;
		    }
		  else
		    {
		      short_name++;
		    }
		}
#ifdef APPLE_HYB
	      if( !insert_file_entry(graft_dir, node, short_name, 0) )
#else
	      if( !insert_file_entry(graft_dir, node, short_name) )
#endif /* APPLE_HYB */
		{
		 exit(1);
		}
	    }
	}

      optind++;
      no_path_names = 0;
    }

  if (pfp && pfp != stdin)
    fclose(pfp);

  /* exit if we don't have any pathnames to process - not going to happen 
     at the moment as we have to have at least one path on the command line */
  if(no_path_names){
          usage();
          exit(1);
  };

  /*
   * Now merge in any previous sessions.  This is driven on the source
   * side, since we may need to create some additional directories.
   */
  if( merge_image != NULL )
    {
      merge_previous_session(root, mrootp);
    }
#ifdef APPLE_HYB
  /* free up any HFS filename mapping memory */
  if (apple_both)
    clean_hfs();
#endif /* APPLE_HYB */

  /* hide "./rr_moved" if all its contents have been hidden */
  if (reloc_dir && i_ishidden())
    hide_reloc_dir();

  /*
   * Sort the directories in the required order (by ISO9660).  Also,
   * choose the names for the 8.3 filesystem if required, and do
   * any other post-scan work.
   */
  goof += sort_tree(root);

  if( use_Joliet )
    {
      goof += joliet_sort_tree(root);
    }

  if (goof)
    {
      fprintf(stderr, "Joliet tree sort failed.\n");
      exit(1);
    }
  
  /*
   * Fix a couple of things in the root directory so that everything
   * is self consistent.
   */
  root->self = root->contents;  /* Fix this up so that the path 
				   tables get done right */

  /*
   * OK, ready to write the file.  Open it up, and generate the thing.
   */
  if (print_size){
	  discimage = fopen("/dev/null", "wb");
	  if (!discimage){
		  fprintf(stderr,"Unable to open /dev/null\n");
		  exit(1);
	  }
  } else if (outfile){
	  discimage = fopen(outfile, "wb");
	  if (!discimage){
		  fprintf(stderr,"Unable to open disc image file\n");
		  exit(1);

	  };
  } else {
	  discimage =  stdout;

#if	defined(__CYGWIN32__)
	setmode(fileno(stdout), O_BINARY);
#endif
  }

  /* Now assign addresses on the disc for the path table. */

  path_blocks = (path_table_size + (SECTOR_SIZE - 1)) >> 11;
  if (path_blocks & 1) path_blocks++;

  jpath_blocks = (jpath_table_size + (SECTOR_SIZE - 1)) >> 11;
  if (jpath_blocks & 1) jpath_blocks++;

  /*
   * Start to set up the linked list that we use to track the
   * contents of the disc.
   */
  outputlist_insert(&padblock_desc);

  /*
   * PVD for disc.
   */
  outputlist_insert(&voldesc_desc);

  /*
   * SVD for El Torito. MUST be immediately after the PVD!
   */
  if( use_eltorito)
    {
      outputlist_insert(&torito_desc);
    }

  /*
   * SVD for Joliet.
   */
  if( use_Joliet)
    {
      outputlist_insert(&joliet_desc);
    }

  /*
   * Finally the last volume desctiptor.
   */
  outputlist_insert(&end_vol);


  outputlist_insert(&pathtable_desc);
  if( use_Joliet)
    {
      outputlist_insert(&jpathtable_desc);
    }

  outputlist_insert(&dirtree_desc);
  if( use_Joliet)
    {
      outputlist_insert(&jdirtree_desc);
    }

  outputlist_insert(&dirtree_clean);

  if(extension_record) 
    { 
      outputlist_insert(&extension_desc);
    }

  outputlist_insert(&files_desc);

  /*
   * Allow room for the various headers we will be writing.  There
   * will always be a primary and an end volume descriptor.
   */
  last_extent = session_start;
  
  /*
   * Calculate the size of all of the components of the disc, and assign
   * extent numbers.
   */
  for(opnt = out_list; opnt; opnt = opnt->of_next )
    {
      if( opnt->of_size != NULL )
	{
	  (*opnt->of_size)(last_extent);
	}
    }

  /*
   * Generate the contents of any of the sections that we want to generate.
   * Not all of the fragments will do anything here - most will generate the
   * data on the fly when we get to the write pass.
   */
  for(opnt = out_list; opnt; opnt = opnt->of_next )
    {
      if( opnt->of_generate != NULL )
	{
	  (*opnt->of_generate)();
	}
    }

  if( in_image != NULL )
    {
      fclose(in_image);
    }

  /*
   * Now go through the list of fragments and write the data that corresponds to
   * each one.
   */
  for(opnt = out_list; opnt; opnt = opnt->of_next )
    {
      if( opnt->of_write != NULL )
	{
	  (*opnt->of_write)(discimage);
	}
    }

  if( verbose > 0 )
    {
#ifdef HAVE_SBRK
      fprintf(stderr,"Max brk space used %x\n", 
	      (unsigned int)(((unsigned long)sbrk(0)) - mem_start));
#endif
      fprintf(stderr,"%d extents written (%d Mb)\n", last_extent, last_extent >> 9);
    }
#ifdef APPLE_HYB
  last_extent += hfs_extra;
#endif /* APPLE_HYB */

#ifdef VMS
  return 1;
#else
  return 0;
#endif
}

/*
 * Find unescaped equal sign in string.
 */
char *
findequal(char *s)
{
	char	*p = s;

	while ((p = strchr(p, '=')) != NULL) {
		if (p > s && p[-1] != '\\')
			return (p);
		p++;
	}
	return (NULL);
}

/*
 * Find unescaped equal sign in string.
 */
char *
escstrcpy(char *to, char *from)
{
	char	*p = to;

	while ((*p = *from++) != '\0') {
		if (*p == '\\' || *p == '=') {
			if (p[-1] == '\\') {
				--p;
				p[0] = p[1];
			}

		}
		p++;
	}
	return (to);
}


void *
FDECL1(e_malloc, size_t, size)
{
void* pt = 0;
	if( (size > 0) && ((pt=malloc(size))==NULL) ) {
		fprintf(stderr, "Not enough memory\n");
		exit (1);
		}
return pt;
}
