/*
 * File tree.c - scan directory  tree and build memory structures for iso9660
 * filesystem

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

static char rcsid[] ="$Id: tree.c,v 1.29 1999/03/07 17:41:19 eric Exp $";

/* ADD_FILES changes made by Ross Biro biro@yggdrasil.com 2/23/95 */

#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <unixstd.h>
#include <fctldefs.h>
#include <device.h>

#ifdef VMS
#include <sys/file.h>
#include <vms/fabdef.h>
#include "vms.h"
extern char * strdup(const char *);
#endif

/*
 * Autoconf should be able to figure this one out for us and let us know
 * whether the system has memmove or not.
 */
# ifndef HAVE_MEMMOVE
#  define memmove(d, s, n) bcopy ((s), (d), (n))
# endif

#include "mkisofs.h"
#include "iso9660.h"
#include "match.h"

#include <sys/stat.h>

#include "exclude.h"

#ifdef	DOESNT_WORK
/*
 * This is the dead code
 */
#ifdef NON_UNIXFS
#define S_ISLNK(m)	(0)
#define S_ISSOCK(m)	(0)
#define S_ISFIFO(m)	(0)
#else
#ifndef S_ISLNK
#define S_ISLNK(m)	(((m) & S_IFMT) == S_IFLNK)
#endif
#ifndef S_ISSOCK
# ifdef S_IFSOCK
#   define S_ISSOCK(m)	(((m) & S_IFMT) == S_IFSOCK)
# else
#   define S_ISSOCK(m)	(0)
# endif
#endif
#endif

#else
/*
 * This is the new code
 */
#include <statdefs.h>
#endif

#ifdef	USE_LIBSCHILY
#include <standard.h>
#endif

#ifdef __SVR4
extern char * strdup(const char *);
#endif

static unsigned char symlink_buff[256];

static char * filetype	__PR((int t));
static char * rstr	__PR((char *s1, char *s2));
static void stat_fix	__PR((struct stat * st));
static void generate_reloc_directory __PR((void));

static void DECL(attach_dot_entries, (struct directory * dirnode,
		   struct stat * parent_stat));
static void DECL(delete_directory, (struct directory * parent, struct directory * child));

extern int verbose;

struct stat fstatbuf = {0,};  /* We use this for the artificial entries we create */

struct stat root_statbuf = {0, };  /* Stat buffer for root directory */

struct directory * reloc_dir = NULL;

static char *
filetype(t)
	int	t;
{
	if ((t & S_IFMT) == 0)		/* 0 (unallocated) */
		return ("unallocated");
	if (S_ISFIFO(t))		/* 1 */
		return ("fifo");
	if (S_ISCHR(t))			/* 2 */
		return ("chr");
	if ((t & S_IFMT) == 3)		/* 3 (multiplexed chr) */
		return ("multiplexed chr");
	if (S_ISDIR(t))			/* 4 */
		return ("dir");
	if ((t & S_IFMT) == 5)		/* 5 (named file) */
		return ("named file");
	if (S_ISBLK(t))			/* 6 */
		return ("blk");
	if ((t & S_IFMT) == 7)		/* 7 (multiplexed blk) */
		return ("multiplexed blk");
	if (S_ISREG(t))			/* 8 */
		return ("regular file");
	if (S_ISCNT(t))			/* 9 */
		return ("contiguous file");
	if (S_ISLNK(t))			/* 10 */
		return ("symlink");
	if ((t & S_IFMT) == 11)		/* 11 (Solaris shadow inode) */
		return ("Solaris shadow inode");
	if (S_ISSOCK(t))		/* 12 */
		return ("socket");
	if (S_ISDOOR(t))		/* 13 */
		return ("door");
	if ((t & S_IFMT) == 14)		/* 14 (CPIO acl) */
		return ("CPIO acl");
	if ((t & S_IFMT) == 15)		/* 15 (unused) */
		return ("unused 15");
	return ("BLETCH");
}

/*
 * Check if s1 ends in strings s2
 */
static char *
rstr(s1, s2)
	char	*s1;
	char	*s2;
{
	int	l1;
	int	l2;

	l1 = strlen(s1);
	l2 = strlen(s2);
	if (l2 > l1)
		return ((char *)NULL);

	if (strcmp(&s1[l1 - l2], s2) == 0)
		return (&s1[l1 - l2]);
	return ((char *)NULL);
}

static void
FDECL1(stat_fix, struct stat *, st)
{
  /* Remove the uid and gid, they will only be useful on the author's
     system.  */
  st->st_uid = 0;
  st->st_gid = 0;

 /*
  * Make sure the file modes make sense.  Turn on all read bits.  Turn
  * on all exec/search bits if any exec/search bit is set.  Turn off
  * all write bits, and all special mode bits (on a r/o fs lock bits
  * are useless, and with uid+gid 0 don't want set-id bits, either).
  */
  st->st_mode |= 0444;
#ifndef _WIN32		/* make all file "executable" */
  if (st->st_mode & 0111)
#endif /* _WIN32 */
    st->st_mode |= 0111;
  st->st_mode &= ~07222;
}

int
FDECL2(stat_filter, char *, path, struct stat *, st)
{
  int result = stat(path, st);
  if (result >= 0 && rationalize)
    stat_fix(st);
  return result;
}

int
FDECL2(lstat_filter, char *, path, struct stat *, st)
{
  int result = lstat(path, st);
  if (result >= 0 && rationalize)
    stat_fix(st);
  return result;
}

static int FDECL1(sort_n_finish, struct directory *, this_dir)
{
  struct directory_entry  * s_entry;
  struct directory_entry  * s_entry1;
  struct directory_entry  * table;
  int			    count;
  int			    d1;
  int			    d2;
  int			    d3;
  int			    new_reclen;
  char			 *  c;
  int			    status = 0;
  int			    tablesize = 0;
  char			    newname[34];
  char			    rootname[34];

  /* Here we can take the opportunity to toss duplicate entries from the
     directory.  */

  /* ignore if it's hidden */
  if(this_dir->dir_flags & INHIBIT_ISO9660_ENTRY)
    {
      return 0;
    }

  table = NULL;

  init_fstatbuf();

  /*
   * If we had artificially created this directory, then we might be
   * missing the required '.' entries.  Create these now if we need
   * them.
   */
  if( (this_dir->dir_flags & (DIR_HAS_DOT | DIR_HAS_DOTDOT)) != 
      (DIR_HAS_DOT | DIR_HAS_DOTDOT) )
    {
      attach_dot_entries(this_dir, &fstatbuf);
    }

  flush_file_hash();
  s_entry = this_dir->contents;
  while(s_entry)
    {
    /* ignore if it's hidden */
    if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY)
      {
	s_entry = s_entry->next;
	continue;
      }
	  
      /*
       * First assume no conflict, and handle this case 
       */
      if(!(s_entry1 = find_file_hash(s_entry->isorec.name)))
	{
	  add_file_hash(s_entry);
	  s_entry = s_entry->next;
	  continue;
	}
	  
      if(s_entry1 == s_entry)
	{
#ifdef	USE_LIBSCHILY
	  comerrno(EX_BAD, "Fatal goof, file '%s' already in hash table.\n", s_entry->isorec.name);
#else
	  fprintf(stderr, "Fatal goof, file '%s' already in hash table.\n", s_entry->isorec.name);
	  exit(1);
#endif
	}
      
      /* 
       * OK, handle the conflicts.  Try substitute names until we come
       * up with a winner 
       */
      strcpy(rootname, s_entry->isorec.name);
      if(full_iso9660_filenames) 
	{
		/*
		 * 27 is 30 chars minus the 3 characters we are
		 * appending below to create unique filenames.
		 */
	  if(strlen(rootname) > 27) rootname[27] = 0;
	}

      /*
       * Strip off the non-significant part of the name so that we are left
       * with a sensible root filename.  If we don't find a '.', then try
       * a ';'.
       */
      c  = strchr(rootname, '.');
	/*
	 * In case we ever allow more than on dot, only modify the section
	 * past the last dot if the file name starts with a dot.
	 */
      if (c != NULL && c == rootname && c != strrchr(rootname, '.'))
	c = strrchr(rootname, '.');
      if (c) 
	*c = 0;
      else
	{
	  c  = strchr(rootname, ';');
	  if (c) *c = 0;
	}
      for(d1 = 0; d1 < 36; d1++)
	{
	  for(d2 = 0; d2 < 36; d2++)
	    {
	      for(d3 = 0; d3 < 36; d3++)
		{
		  sprintf(newname,"%s.%c%c%c%s", rootname,  
			  (d1 <= 9 ? '0' + d1 : 'A' + d1 - 10),
			  (d2 <= 9 ? '0' + d2 : 'A' + d2 - 10),
			  (d3 <= 9 ? '0' + d3 : 'A' + d3 - 10),
			  (s_entry->isorec.flags[0] == 2 || 
			   omit_version_number ? "" : ";1"));
		  
#ifdef VMS
		  /* Sigh.  VAXCRTL seems to be broken here */
		  {
		    int ijk = 0;
		    while(newname[ijk]) 
		      {
			if(newname[ijk] == ' ') newname[ijk] = '0';
			ijk++;
		      }
		  }
#endif
		  
		  if(!find_file_hash(newname)) goto got_valid_name;
		}
	    }
	}

      /*
       * If we fell off the bottom here, we were in real trouble.
       */
#ifdef	USE_LIBSCHILY
      comerrno(EX_BAD, "Unable to generate unique name for file %s\n", s_entry->name);
#else
      fprintf(stderr,"Unable to generate unique name for file %s\n", s_entry->name);
      exit(1);
#endif

got_valid_name:      
      /* 
       * OK, now we have a good replacement name.  Now decide which one
       * of these two beasts should get the name changed 
       */
      if(s_entry->priority < s_entry1->priority) 
	{
	  if( verbose > 0 )
	    {
	      fprintf(stderr,"Using %s for  %s%s%s (%s)\n", newname,  
		      this_dir->whole_name, SPATH_SEPARATOR, 
		      s_entry->name, s_entry1->name);
	    }
	  s_entry->isorec.name_len[0] =  strlen(newname);
	  new_reclen =  offsetof(struct iso_directory_record, name[0]) +
	    strlen(newname);
	  if(use_RockRidge) 
	    {
	      if (new_reclen & 1) new_reclen++;  /* Pad to an even byte */
	      new_reclen += s_entry->rr_attr_size;
	    }
	  if (new_reclen & 1) new_reclen++;  /* Pad to an even byte */
	  s_entry->isorec.length[0] = new_reclen;
	  strcpy(s_entry->isorec.name, newname);
	}
      else 
	{
	  delete_file_hash(s_entry1);
	  if( verbose > 0 )
	    {
	      fprintf(stderr,"Using %s for  %s%s%s (%s)\n", newname,  
		      this_dir->whole_name, SPATH_SEPARATOR, 
		      s_entry1->name, s_entry->name);
	    }
	  s_entry1->isorec.name_len[0] =  strlen(newname);
	  new_reclen =  offsetof(struct iso_directory_record, name[0]) +
	    strlen(newname);
	  if(use_RockRidge) 
	    {
	      if (new_reclen & 1) new_reclen++;  /* Pad to an even byte */
	      new_reclen += s_entry1->rr_attr_size;
	    }
	  if (new_reclen & 1) new_reclen++;  /* Pad to an even byte */
	  s_entry1->isorec.length[0] = new_reclen;
	  strcpy(s_entry1->isorec.name, newname);
	  add_file_hash(s_entry1);
	}
      add_file_hash(s_entry);
      s_entry = s_entry->next;
    }

  if(generate_tables 
     && !find_file_hash(trans_tbl) 
     && (reloc_dir != this_dir)
     && (this_dir->extent == 0) )
    {
      /* 
       * First we need to figure out how big this table is 
       */
      for (s_entry = this_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) continue;
	  if(s_entry->table) tablesize += 35 + strlen(s_entry->table);
	}
    }

  if( tablesize > 0 )
    {
      table = (struct directory_entry *) 
	e_malloc(sizeof (struct directory_entry));
      memset(table, 0, sizeof(struct directory_entry));
      table->table = NULL;
      table->next = this_dir->contents;
      this_dir->contents = table;
      
      table->filedir = root;
      table->isorec.flags[0] = 0;
      table->priority  = 32768;
      iso9660_date(table->isorec.date, fstatbuf.st_mtime);
      table->inode = TABLE_INODE;
      table->dev = (dev_t) UNCACHED_DEVICE;
      set_723(table->isorec.volume_sequence_number, volume_sequence_number);
      set_733((char *) table->isorec.size, tablesize);
      table->size = tablesize;
      table->filedir = this_dir;
      if (jhide_trans_tbl)
      	table->de_flags    |= INHIBIT_JOLIET_ENTRY;
/*      table->name = strdup("<translation table>");*/
      table->name = strdup(trans_tbl);
      table->table = (char *) e_malloc(ROUND_UP(tablesize));
      memset(table->table, 0, ROUND_UP(tablesize));
      iso9660_file_length  (trans_tbl, table, 0);
      
      if(use_RockRidge)
	{
	  fstatbuf.st_mode = 0444 | S_IFREG;
	  fstatbuf.st_nlink = 1;
	  generate_rock_ridge_attributes("",
					 trans_tbl, table,
					 &fstatbuf, &fstatbuf, 0);
	}
    }
  
  /*
   * We have now chosen the 8.3 names and we should now know the length
   * of every entry in the directory.
   */
  for(s_entry = this_dir->contents; s_entry; s_entry = s_entry->next)
    {
      /* skip if it's hidden */
      if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY)
	{
	  continue;
	}

      new_reclen = strlen(s_entry->isorec.name);
	  
      /*
       * First update the path table sizes for directories.
       */
      if(s_entry->isorec.flags[0] ==  2)
	{
	  if (strcmp(s_entry->name,".") != 0 && strcmp(s_entry->name,"..") != 0)
	    {
	      path_table_size += new_reclen + offsetof(struct iso_path_table, name[0]);
	      if (new_reclen & 1) path_table_size++;
	    }
	  else 
	    {
	      new_reclen = 1;
	      if (this_dir == root && strlen(s_entry->name) == 1)
		{
		  path_table_size += new_reclen + offsetof(struct iso_path_table, name[0]);
		}
	    }
	}
      if(path_table_size & 1) path_table_size++;  /* For odd lengths we pad */
      s_entry->isorec.name_len[0] = new_reclen;
      
      new_reclen += offsetof(struct iso_directory_record, name[0]);
      
      if (new_reclen & 1)	
	new_reclen++;
      
      new_reclen += s_entry->rr_attr_size;
      
      if (new_reclen & 1) new_reclen++;
      
      if(new_reclen > 0xff) 
	{
#ifdef	USE_LIBSCHILY
	  comerrno(EX_BAD, "Fatal error - RR overflow for file %s\n",
		  s_entry->name);
#else
	  fprintf(stderr,"Fatal error - RR overflow for file %s\n",
		  s_entry->name);
	  exit(1);
#endif
	}
      s_entry->isorec.length[0] = new_reclen;
    }

  status = sort_directory(&this_dir->contents, (reloc_dir == this_dir));
  if( status > 0 )
    {
#ifdef	USE_LIBSCHILY
      comerrno(EX_BAD, "Unable to sort directory %s\n", 
	      this_dir->whole_name);
#else
      fprintf(stderr, "Unable to sort directory %s\n", 
	      this_dir->whole_name);
	exit(1);
#endif
    }

  /*
   * If we are filling out a TRANS.TBL, generate the entries that will
   * go in the thing.
   */
  if(table)
    {
      count = 0;
      for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next){
	if(s_entry == table) continue;
	if(!s_entry->table) continue;
	if(strcmp(s_entry->name, ".") == 0  ||
	   strcmp(s_entry->name, "..") == 0) continue;
	if(s_entry->de_flags & INHIBIT_ISO9660_ENTRY) continue;
	/*
	 * Warning: we cannot use the return value of sprintf because
	 * old BSD based sprintf() implementations will return
	 * a pointer to the result instead of a count.
	 */
	sprintf(table->table + count, "%c %-34s%s",
		s_entry->table[0],
		s_entry->isorec.name, s_entry->table+1);
	count += strlen(table->table + count);
	free(s_entry->table);
	/* for a memory file, set s_entry->table to the correct data -
	   which is stored in s_entry->whole_name */
	if (s_entry->de_flags & MEMORY_FILE) {
	    s_entry->table = s_entry->whole_name;
	    s_entry->whole_name = NULL;
	} else {
	    s_entry->table = NULL;
	}
      }

      if(count !=  tablesize) 
	{
#ifdef	USE_LIBSCHILY
	  comerrno(EX_BAD, "Translation table size mismatch %d %d\n",
		  count, tablesize);
#else
	  fprintf(stderr,"Translation table size mismatch %d %d\n",
		  count, tablesize);
	  exit(1);
#endif
	}
    }

  /* 
   * Now go through the directory and figure out how large this one will be.
   * Do not split a directory entry across a sector boundary 
   */
  s_entry = this_dir->contents;
  this_dir->ce_bytes = 0;
  while(s_entry)
    {
      /* skip if it's hidden */
      if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) {
	s_entry = s_entry->next;
	continue;
      }

      new_reclen = s_entry->isorec.length[0];
      if ((this_dir->size & (SECTOR_SIZE - 1)) + new_reclen >= SECTOR_SIZE)
	this_dir->size = (this_dir->size + (SECTOR_SIZE - 1)) & 
	~(SECTOR_SIZE - 1);
      this_dir->size += new_reclen;

      /* See if continuation entries were used on disc */
      if(use_RockRidge && 
	 s_entry->rr_attr_size != s_entry->total_rr_attr_size) 
	{
	  unsigned char * pnt;
	  int len;
	  int nbytes;
	  
	  pnt = s_entry->rr_attributes;
	  len = s_entry->total_rr_attr_size;
	  
	  /*
	   * We make sure that each continuation entry record is not
	   * split across sectors, but each file could in theory have more
	   * than one CE, so we scan through and figure out what we need. 
	   */
	  while(len > 3)
	    {
	      if(pnt[0] == 'C' && pnt[1] == 'E') 
		{
		  nbytes = get_733((char *) pnt+20);
		  
		  if((this_dir->ce_bytes & (SECTOR_SIZE - 1)) + nbytes >=
		     SECTOR_SIZE) this_dir->ce_bytes = 
				    ROUND_UP(this_dir->ce_bytes);
		  /* Now store the block in the ce buffer */
		  this_dir->ce_bytes += nbytes;
		  if(this_dir->ce_bytes & 1) this_dir->ce_bytes++;
		}
	      len -= pnt[2];
	      pnt += pnt[2];
	    }
	}
      s_entry = s_entry->next;
    }
  return status;
}

static void generate_reloc_directory()
{
	time_t current_time;
	struct directory_entry  *s_entry;

	/* Create an  entry for our internal tree */
	time (&current_time);
	reloc_dir = (struct directory *) 
		e_malloc(sizeof(struct directory));
	memset(reloc_dir, 0, sizeof(struct directory));
	reloc_dir->parent = root;
	reloc_dir->next = root->subdir;
	root->subdir = reloc_dir;
	reloc_dir->depth = 1;
	if (hide_rr_moved) {
		reloc_dir->whole_name = strdup("./.rr_moved");
		reloc_dir->de_name =  strdup(".rr_moved");
	} else {
		reloc_dir->whole_name = strdup("./rr_moved");
		reloc_dir->de_name =  strdup("rr_moved");
	}
	reloc_dir->extent = 0;
	
	
	/* Now create an actual directory  entry */
	s_entry = (struct directory_entry *) 
		e_malloc(sizeof (struct directory_entry));
	memset(s_entry, 0, sizeof(struct directory_entry));
	s_entry->next = root->contents;
	reloc_dir->self = s_entry;

	/*
	 * The rr_moved entry will not appear in the Joliet tree.
	 */
	reloc_dir->dir_flags |= INHIBIT_JOLIET_ENTRY;
	s_entry->de_flags    |= INHIBIT_JOLIET_ENTRY;

	/*
	 * Hiding RR_MOVED seems not to be possible.....
	 */
#ifdef	HIDE_RR
	reloc_dir->dir_flags |= INHIBIT_ISO9660_ENTRY;
	s_entry->de_flags    |= INHIBIT_ISO9660_ENTRY;
#endif

	root->contents = s_entry;
	root->contents->name = strdup(reloc_dir->de_name);
	root->contents->filedir = root;
	root->contents->isorec.flags[0] = 2;
	root->contents->priority  = 32768;
	iso9660_date(root->contents->isorec.date, current_time);
	root->contents->inode = UNCACHED_INODE;
	root->contents->dev = (dev_t) UNCACHED_DEVICE;
	set_723(root->contents->isorec.volume_sequence_number, volume_sequence_number);
	iso9660_file_length (reloc_dir->de_name, root->contents, 1);

	init_fstatbuf();

	if(use_RockRidge) {
		fstatbuf.st_mode = 0555 | S_IFDIR;
		fstatbuf.st_nlink = 2;
		generate_rock_ridge_attributes("",
				       hide_rr_moved?".rr_moved":"rr_moved",
					s_entry, &fstatbuf, &fstatbuf, 0);
	};
	
	/* Now create the . and .. entries in rr_moved */
	/* Now create an actual directory  entry */
	attach_dot_entries(reloc_dir, &root_statbuf);
}

/* 
 * Function:		attach_dot_entries
 *
 * Purpose:		Create . and .. entries for a new directory.
 *
 * Notes:		Only used for artificial directories that
 *			we are creating.
 */
static void FDECL2(attach_dot_entries, struct directory *, dirnode,
		   struct stat *, parent_stat)
{
	struct directory_entry  *s_entry;
	struct directory_entry  *orig_contents;
	int deep_flag = 0;

	init_fstatbuf();

	orig_contents = dirnode->contents;

	if( (dirnode->dir_flags & DIR_HAS_DOTDOT) == 0 )
	  {
	    s_entry = (struct directory_entry *) 
	      e_malloc(sizeof (struct directory_entry));
	    memcpy(s_entry, dirnode->self, 
		   sizeof(struct directory_entry));
	    s_entry->name = strdup("..");
	    s_entry->whole_name = NULL;
	    s_entry->isorec.name_len[0] = 1;
	    s_entry->isorec.flags[0] = 2; /* Mark as a directory */
	    iso9660_file_length ("..", s_entry, 1);
	    iso9660_date(s_entry->isorec.date, fstatbuf.st_mtime);
	    set_723(s_entry->isorec.volume_sequence_number, volume_sequence_number);
	    set_733(s_entry->isorec.size,SECTOR_SIZE);
	    memset(s_entry->isorec.extent, 0, 8);
	    s_entry->filedir = dirnode->parent;

	    dirnode->contents = s_entry;
	    dirnode->contents->next = orig_contents;
	    orig_contents = s_entry;

	    if(use_RockRidge)
	      {
		if( parent_stat == NULL )
		  {
		    parent_stat = &fstatbuf;
		  }
		generate_rock_ridge_attributes("",
					       "..", s_entry,
					       parent_stat, 
					       parent_stat, 0);
	      }
	    dirnode->dir_flags |= DIR_HAS_DOTDOT;
	  }

	if( (dirnode->dir_flags & DIR_HAS_DOT) == 0 )
	  {
	    s_entry = (struct directory_entry *) 
	      e_malloc(sizeof (struct directory_entry));
	    memcpy(s_entry, dirnode->self, 
		   sizeof(struct directory_entry));
	    s_entry->name = strdup(".");
	    s_entry->whole_name = NULL;
	    s_entry->isorec.name_len[0] = 1;
	    s_entry->isorec.flags[0] = 2; /* Mark as a directory */
	    iso9660_file_length (".", s_entry, 1);
	    iso9660_date(s_entry->isorec.date, fstatbuf.st_mtime);
	    set_723(s_entry->isorec.volume_sequence_number, volume_sequence_number);
	    set_733(s_entry->isorec.size,SECTOR_SIZE);
	    memset(s_entry->isorec.extent, 0, 8);
	    s_entry->filedir = dirnode;
	    
	    dirnode->contents = s_entry;
	    dirnode->contents->next = orig_contents;

	    if(use_RockRidge)
	      {
		fstatbuf.st_mode = 0555 | S_IFDIR;
		fstatbuf.st_nlink = 2;
		
		if( dirnode == root )
		  {
		    deep_flag |= NEED_CE | NEED_SP;  /* For extension record */
		  }
		
		generate_rock_ridge_attributes("",
					       ".", s_entry,
					       &fstatbuf, &fstatbuf, deep_flag);
	      }
	    
	    dirnode->dir_flags |= DIR_HAS_DOT;
	  }

}

static void FDECL2(update_nlink, struct directory_entry *, s_entry, int, value)
{
    unsigned char * pnt;
    int len;
    
    pnt = s_entry->rr_attributes;
    len = s_entry->total_rr_attr_size;
    while(len)
    {
	if(pnt[0] == 'P' && pnt[1] == 'X') 
	{
	    set_733((char *) pnt+12, value);
	    break;
	}
	len -= pnt[2];
	pnt += pnt[2];
    }
}

static void FDECL1(increment_nlink, struct directory_entry *, s_entry)
{
    unsigned char * pnt;
    int len, nlink;
    
    pnt = s_entry->rr_attributes;
    len = s_entry->total_rr_attr_size;
    while(len)
    {
	if(pnt[0] == 'P' && pnt[1] == 'X') 
	{
	    nlink =  get_733((char *) pnt+12);
	    set_733((char *) pnt+12, nlink+1);
	    break;
	}
	len -= pnt[2];
	pnt += pnt[2];
    }
}

void finish_cl_pl_entries(){
  struct directory_entry  *s_entry, *s_entry1;
  struct directory *  d_entry;

  /* if the reloc_dir is hidden (empty), then return */
  if (reloc_dir->dir_flags & INHIBIT_ISO9660_ENTRY)
    return;

  s_entry = reloc_dir->contents;
   s_entry  = s_entry->next->next;  /* Skip past . and .. */
  for(; s_entry; s_entry = s_entry->next){
	  /* skip if it's hidden */
	  if(s_entry->de_flags & INHIBIT_ISO9660_ENTRY) {
	    continue;
	  }
	  d_entry = reloc_dir->subdir;
	  while(d_entry){
		  if(d_entry->self == s_entry) break;
		  d_entry = d_entry->next;
	  };
	  if(!d_entry){
#ifdef	USE_LIBSCHILY
		  comerrno(EX_BAD, "Unable to locate directory parent\n");
#else
		  fprintf(stderr,"Unable to locate directory parent\n");
		  exit(1);
#endif
	  };

	  /* First fix the PL pointer in the directory in the rr_reloc dir */
	  s_entry1 = d_entry->contents->next;
	  set_733((char *) s_entry1->rr_attributes +  s_entry1->total_rr_attr_size - 8,
		  s_entry->filedir->extent);

	  /* Now fix the CL pointer */
	  s_entry1 = s_entry->parent_rec;

	  set_733((char *) s_entry1->rr_attributes +  s_entry1->total_rr_attr_size - 8,
		  d_entry->extent);

	  s_entry->filedir = reloc_dir;  /* Now we can fix this */
  }
  /* Next we need to modify the NLINK terms in the assorted root directory records
     to account for the presence of the RR_MOVED directory */

  increment_nlink(root->self);
  increment_nlink(root->self->next);
  d_entry = root->subdir;
  while(d_entry){
    increment_nlink(d_entry->contents->next);
    d_entry = d_entry->next;
  };
}

/* 
 * Function:		scan_directory_tree
 *
 * Purpose:		Walk through a directory on the local machine
 *			filter those things we don't want to include
 *			and build our representation of a dir.
 *
 * Notes:
 */
int
FDECL3(scan_directory_tree,struct directory *, this_dir,
       char *, path, 
       struct directory_entry *, de)
{
  DIR				* current_dir;
  char				  whole_path[1024];
  struct dirent			* d_entry;
  struct directory		* parent;
  int				  dflag;
  char				* old_path;

    if (verbose > 1)
    {
      fprintf(stderr, "Scanning %s\n", path);
    }

  current_dir = opendir(path);
  d_entry = NULL;

  /* Apparently NFS sometimes allows you to open the directory, but
     then refuses to allow you to read the contents.  Allow for this */

  old_path = path;

  if(current_dir) d_entry = readdir(current_dir);

  if(!current_dir || !d_entry) 
    {
	int	ret = 1;

#ifdef	USE_LIBSCHILY
      errmsg("Unable to open directory %s\n", path);
#else
      fprintf(stderr,"Unable to open directory %s\n", path);
#endif
	if (errno == ENOTDIR) {
		de->isorec.flags[0] &= ~2; /* Mark as not a directory */
		ret = 0;
	}
      if(current_dir) closedir(current_dir);
      return ret;
    }

#ifdef	ABORT_DEEP_ISO_ONLY
	if ((this_dir->depth > RR_relocation_depth) && !use_RockRidge) {
		errmsgno(EX_BAD, "Directories too deep for '%s' (%d) max is %d.\n",
				path, this_dir->depth, RR_relocation_depth);
		closedir(current_dir);
		return 1;
	}
#endif

  parent = de->filedir;
  /* Set up the struct for the current directory, and insert it into the
     tree */

#ifdef VMS
  vms_path_fixup(path);
#endif

  /*
   * if entry for this sub-directory is hidden, then hide this directory
   */
  if (de->de_flags & INHIBIT_ISO9660_ENTRY)
    this_dir->dir_flags |= INHIBIT_ISO9660_ENTRY;
  
  if (de->de_flags & INHIBIT_JOLIET_ENTRY)
    this_dir->dir_flags |= INHIBIT_JOLIET_ENTRY;
  
  /* 
   * Now we scan the directory itself, and look at what is inside of it. 
   */
  dflag = 0;
  while(1==1){

    /* The first time through, skip this, since we already asked for
       the first entry when we opened the directory. */
    if(dflag) d_entry = readdir(current_dir);
    dflag++;

    if(!d_entry) break;

    /* OK, got a valid entry */

    /* If we do not want all files, then pitch the backups. */
    if(!all_files){
	    if(   strchr(d_entry->d_name,'~')
	       || strchr(d_entry->d_name,'#')
	       || rstr(d_entry->d_name,".bak"))
	      {
		if( verbose > 0 )
		  {
		    fprintf(stderr, "Ignoring file %s\n", d_entry->d_name);
		  }
		continue;
	      }
    }

    if(strlen(path)+strlen(d_entry->d_name) + 2 > sizeof(whole_path)){
#ifdef	USE_LIBSCHILY
      comerrno(EX_BAD, "Overflow of stat buffer\n");
#else
      fprintf(stderr, "Overflow of stat buffer\n");
      exit(1);
#endif
    };

    /* Generate the complete ASCII path for this file */
    strcpy(whole_path, path);
#ifndef VMS
    if(whole_path[strlen(whole_path)-1] != '/')
      strcat(whole_path, "/");
#endif
    strcat(whole_path, d_entry->d_name);

    /** Should we exclude this file ? */
    if (matches(d_entry->d_name) || matches(whole_path)) {
      if (verbose > 1) {
	fprintf(stderr, "Excluded by match: %s\n", whole_path);
      }
      continue;
    }

    if(    generate_tables 
	&& strcmp(d_entry->d_name, trans_tbl) == 0 )
      {
	/*
	 * Ignore this entry.  We are going to be generating new
	 * versions of these files, and we need to ignore any
	 * originals that we might have found.
	 */
	if (verbose > 1) 
	  {
	    fprintf(stderr, "Excluded: %s\n",whole_path);
	  }
	continue;
      }

    /*
     * If we already have a '.' or a '..' entry, then don't
     * insert new ones.
     */
    if( strcmp(d_entry->d_name, ".") == 0 
	&& this_dir->dir_flags & DIR_HAS_DOT )
      {
	continue;
      }

    if( strcmp(d_entry->d_name, "..") == 0 
	&& this_dir->dir_flags & DIR_HAS_DOTDOT )
      {
	continue;
      }

#if 0
    if (verbose > 1)  fprintf(stderr, "%s\n",whole_path);
#endif
    /*
     * This actually adds the entry to the directory in question.
     */
    insert_file_entry(this_dir, whole_path, d_entry->d_name);
  }
  closedir(current_dir);
  
  return 1;
}


/* 
 * Function:		insert_file_entry
 *
 * Purpose:		Insert one entry into our directory node.
 *
 * Note:
 * This function inserts a single entry into the directory.  It
 * is assumed that all filtering and decision making regarding what
 * we want to include has already been made, so the purpose of this
 * is to insert one entry (file, link, dir, etc), into this directory.
 * Note that if the entry is a dir (or if we are following links,
 * and the thing it points to is a dir), then we will scan those
 * trees before we return.
 */
int
FDECL3(insert_file_entry,struct directory *, this_dir,
       char *, whole_path,
       char *, short_name)
{
  struct stat			  statbuf, lstatbuf;
  struct directory_entry	* s_entry, *s_entry1;
  int				  lstatus;
  int				  status;
  int				  deep_flag;
  int				  no_scandir = 0;

  status = stat_filter(whole_path, &statbuf);

  lstatus = lstat_filter(whole_path, &lstatbuf);

  if( (status == -1) && (lstatus == -1) )
    {
      /*
       * This means that the file doesn't exist, or isn't accessible.
       * Sometimes this is because of NFS permissions problems.
       */
#ifdef	USE_LIBSCHILY
      errmsg("Non-existant or inaccessible: %s\n",whole_path);
#else
      fprintf(stderr, "Non-existant or inaccessible: %s\n",whole_path);
#endif
      return 0;
    }
  
  if(this_dir == root && strcmp(short_name, ".") == 0)
    root_statbuf = statbuf;  /* Save this for later on */
  
  /* We do this to make sure that the root entries are consistent */
  if(this_dir == root && strcmp(short_name, "..") == 0) 
    {
      statbuf = root_statbuf;
      lstatbuf = root_statbuf;
    }

  if(S_ISLNK(lstatbuf.st_mode))
    {
      
      /* Here we decide how to handle the symbolic links.  Here
	 we handle the general case - if we are not following
	 links or there is an error, then we must change
	 something.  If RR is in use, it is easy, we let RR
	 describe the file.  If not, then we punt the file. */
      
      if((status || !follow_links))
	{
	  if(use_RockRidge)
	    {
	      status = 0;
	      statbuf.st_size = 0;
	      STAT_INODE(statbuf) = UNCACHED_INODE;
	      statbuf.st_dev = (dev_t) UNCACHED_DEVICE;
	      statbuf.st_mode = (statbuf.st_mode & ~S_IFMT) | S_IFREG;
	    } else {
	      if(follow_links) 
		{
#ifdef	USE_LIBSCHILY
/* XXX errno may be wrong! */
		  errmsg("Unable to stat file %s - ignoring and continuing.\n",
			  whole_path);
#else
		  fprintf(stderr,
			  "Unable to stat file %s - ignoring and continuing.\n",
			  whole_path);
#endif
		}
	      else
		{
#ifdef	USE_LIBSCHILY
		  errmsgno(EX_BAD,
			  "Symlink %s ignored - continuing.\n",
			  whole_path);
#else
		  fprintf(stderr,
			  "Symlink %s ignored - continuing.\n",
			  whole_path);
#endif
		  return 0;  /* Non Rock Ridge discs - ignore all symlinks */
		}
	    }
	}
      
      /* Here we handle a different kind of case.  Here we have
	 a symlink, but we want to follow symlinks.  If we run
	 across a directory loop, then we need to pretend that
	 we are not following symlinks for this file.  If this
	 is the first time we have seen this, then make this
	 seem as if there was no symlink there in the first
	 place */
      
      if( follow_links
	  && S_ISDIR(statbuf.st_mode) ) 
	{
	  if(   strcmp(short_name, ".")
		&& strcmp(short_name, "..") )
	    {
	      if(find_directory_hash(statbuf.st_dev, STAT_INODE(statbuf)))
		{
		  if(!use_RockRidge) 
		    {
		      fprintf(stderr, "Already cached directory seen (%s)\n", 
			      whole_path);
		      return 0;
		    }
		  lstatbuf = statbuf;
		  no_scandir = 1;
		}
	      else 
		{
		  lstatbuf = statbuf;
		  add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf));
		}
	    }
	}
      
      /*
       * For non-directories, we just copy the stat information over
       * so we correctly include this file.
       */
      if( follow_links
	  && !S_ISDIR(statbuf.st_mode) ) 
	{
	  lstatbuf = statbuf;
	}
    }
  
  /*
   * Add directories to the cache so that we don't waste space even
   * if we are supposed to be following symlinks.
   */
  if( follow_links
      && strcmp(short_name, ".")
      && strcmp(short_name, "..")
      && S_ISDIR(statbuf.st_mode) ) 
    {
      add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf));
    }
#ifdef VMS
  if(!S_ISDIR(lstatbuf.st_mode) && (statbuf.st_fab_rfm != FAB$C_FIX && 
				    statbuf.st_fab_rfm != FAB$C_STMLF)) {
    fprintf(stderr,"Warning - file %s has an unsupported VMS record"
	    " format (%d)\n",
	    whole_path, statbuf.st_fab_rfm);
  }
#endif
  
  if(S_ISREG(lstatbuf.st_mode) && (status = access(whole_path, R_OK)))
    {
#ifdef	USE_LIBSCHILY
      errmsg("File %s is not readable - ignoring\n", 
	      whole_path);
#else
      fprintf(stderr, "File %s is not readable (errno = %d) - ignoring\n", 
	      whole_path, errno);
#endif
      return 0;
    }
  
  /* Add this so that we can detect directory loops with hard links.
     If we are set up to follow symlinks, then we skip this checking. */
  if(   !follow_links 
	&& S_ISDIR(lstatbuf.st_mode) 
	&& strcmp(short_name, ".") 
	&& strcmp(short_name, "..") ) 
    {
      if(find_directory_hash(statbuf.st_dev, STAT_INODE(statbuf))) {
#ifdef	USE_LIBSCHILY
/*	comerrno(EX_BAD, "Directory loop - fatal goof (%s %lx %lu).\n",*/
	comerrno(EX_BAD, "Warning: Directory loop (%s dev: %lx ino: %lu).\n",
		whole_path, (unsigned long) statbuf.st_dev,
		(unsigned long) STAT_INODE(statbuf));
#else
/*	fprintf(stderr,"Directory loop - fatal goof (%s %lx %lu).\n",*/
	fprintf(stderr, "Warning: Directory loop (%s dev: %lx ino: %lu).\n",
		whole_path, (unsigned long) statbuf.st_dev,
		(unsigned long) STAT_INODE(statbuf));
	exit(1);
#endif
      }
      add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf));
    }
  
  if (!S_ISCHR(lstatbuf.st_mode) && !S_ISBLK(lstatbuf.st_mode) &&
      !S_ISFIFO(lstatbuf.st_mode) && !S_ISSOCK(lstatbuf.st_mode)
      && !S_ISLNK(lstatbuf.st_mode) && !S_ISREG(lstatbuf.st_mode) &&
      !S_ISDIR(lstatbuf.st_mode)) {
    fprintf(stderr,"Unknown file type (%s) %s - ignoring and continuing.\n",
	    filetype((int)lstatbuf.st_mode), whole_path);
    return 0;
  }
  
  /* Who knows what trash this is - ignore and continue */
  
  if(status) 
    {
#ifdef	USE_LIBSCHILY
      errmsg("Unable to stat file %s - ignoring and continuing.\n",
	      whole_path);
#else
      fprintf(stderr,
	      "Unable to stat file %s - ignoring and continuing.\n",
	      whole_path);
#endif
      return 0; 
    }
  
  /*
   * Check to see if we have already seen this directory node.
   * If so, then we don't create a new entry for it, but we do want
   * to recurse beneath it and add any new files we do find.
   */
  if (S_ISDIR(statbuf.st_mode)) 
    {
      int dflag;
      
      for( s_entry = this_dir->contents; s_entry; s_entry = s_entry->next)
	{
	  if( strcmp(s_entry->name, short_name) == 0 )
	    {
	      break;
	    }
	}
      if ( s_entry != NULL 
	   && strcmp(short_name,".") 
	   && strcmp(short_name,"..")) 
	{
	  struct directory * child;
	  
	  if ( (s_entry->de_flags & RELOCATED_DIRECTORY) != 0)
	    {
	      for( s_entry = reloc_dir->contents; s_entry; s_entry = s_entry->next)
		{
		  if( strcmp(s_entry->name, short_name) == 0 )
		    {
		      break;
		    }
		}
	      child = find_or_create_directory(reloc_dir, whole_path, 
					       s_entry, 1);
	    }
	  else
	    {
	      child = find_or_create_directory(this_dir, whole_path, 
					       s_entry, 1);
	      /* If unable to scan directory, mark this as a non-directory */
	    }
	  if (no_scandir) dflag = 1; else
	  dflag = scan_directory_tree(child, whole_path, s_entry);
	  if(!dflag)
	    {
	      lstatbuf.st_mode = (lstatbuf.st_mode & ~S_IFMT) | S_IFREG;
	    }
	  return 0;
	}
    }
  
  s_entry = (struct directory_entry *) 
    e_malloc(sizeof (struct directory_entry));
  s_entry->next = this_dir->contents;
  memset(s_entry->isorec.extent, 0, 8);
  this_dir->contents = s_entry;
  deep_flag = 0;
  s_entry->table = NULL;
  
  s_entry->name = strdup(short_name);
  s_entry->whole_name = strdup (whole_path);

  s_entry->de_flags = 0;

  /*
   * If the current directory is hidden, then hide all it's members
   * otherwise check if this entry needs to be hidden as well */
  if (this_dir->dir_flags & INHIBIT_ISO9660_ENTRY) {
    s_entry->de_flags |= INHIBIT_ISO9660_ENTRY;
  }
  else if (strcmp(short_name,".") != 0 && strcmp(short_name,"..") != 0) {
    if (i_matches(short_name) || i_matches(whole_path)) {
      if (verbose > 1) {
	fprintf(stderr, "Hidden from ISO9660 tree: %s\n", whole_path);
      }
      s_entry->de_flags |= INHIBIT_ISO9660_ENTRY;
    }
  }

  if (this_dir != reloc_dir && this_dir->dir_flags & INHIBIT_JOLIET_ENTRY) {
    s_entry->de_flags |= INHIBIT_JOLIET_ENTRY;
  }
  else if (strcmp(short_name,".") != 0 && strcmp(short_name,"..") != 0) {
    if (j_matches(short_name) || j_matches(whole_path)) {
      if (verbose > 1) {
	fprintf(stderr, "Hidden from Joliet tree: %s\n", whole_path);
      }
      s_entry->de_flags |= INHIBIT_JOLIET_ENTRY;
    }
  }

  s_entry->filedir = this_dir;
  s_entry->isorec.flags[0] = 0;
  s_entry->isorec.ext_attr_length[0] = 0;
  iso9660_date(s_entry->isorec.date, statbuf.st_mtime);
  s_entry->isorec.file_unit_size[0] = 0;
  s_entry->isorec.interleave[0] = 0;

  if( strcmp(short_name,  ".") == 0)
    {
      this_dir->dir_flags |= DIR_HAS_DOT;
    } 

  if( strcmp(short_name,  "..") == 0)
    {
      this_dir->dir_flags |= DIR_HAS_DOTDOT;
    } 

  if(   this_dir->parent 
     && this_dir->parent == reloc_dir 
     && strcmp(short_name,  "..") == 0)
    {
      s_entry->inode = UNCACHED_INODE;
      s_entry->dev = (dev_t) UNCACHED_DEVICE;
      deep_flag  = NEED_PL;
    } 
  else
    {
      s_entry->inode = STAT_INODE(statbuf);
      s_entry->dev = statbuf.st_dev;
    }
  set_723(s_entry->isorec.volume_sequence_number, volume_sequence_number);
  iso9660_file_length(short_name, s_entry, S_ISDIR(statbuf.st_mode));
  s_entry->rr_attr_size = 0;
  s_entry->total_rr_attr_size = 0;
  s_entry->rr_attributes = NULL;
  
  /* Directories are assigned sizes later on */
  if (!S_ISDIR(statbuf.st_mode)) 
    {
      if (S_ISCHR(lstatbuf.st_mode) || S_ISBLK(lstatbuf.st_mode) || 
	  S_ISFIFO(lstatbuf.st_mode) || S_ISSOCK(lstatbuf.st_mode)
	  || S_ISLNK(lstatbuf.st_mode))
	{
	  s_entry->size = 0; 
	  statbuf.st_size = 0; 
	}
      else
	{
	  s_entry->size = statbuf.st_size; 
	}

      set_733((char *) s_entry->isorec.size, statbuf.st_size); 
    } 
  else
    {
      s_entry->isorec.flags[0] = 2;
    }
  
  if (strcmp(short_name,".") != 0 && strcmp(short_name,"..") != 0 &&
      S_ISDIR(statbuf.st_mode) && this_dir->depth >  RR_relocation_depth)
    {
      struct directory * child;

      if(!reloc_dir) generate_reloc_directory();
      
      /*
       * Replicate the entry for this directory.  The old one will stay where it
       * is, and it will be neutered so that it no longer looks like a directory.
       * The new one will look like a directory, and it will be put in the reloc_dir.
       */
      s_entry1 = (struct directory_entry *) 
	e_malloc(sizeof (struct directory_entry));
      memcpy(s_entry1, s_entry,  sizeof(struct directory_entry));
      s_entry1->table = NULL;
      s_entry1->name = strdup(this_dir->contents->name);
      s_entry1->whole_name = strdup(this_dir->contents->whole_name);
      s_entry1->next = reloc_dir->contents;
      reloc_dir->contents = s_entry1;
      s_entry1->priority  =  32768;
      s_entry1->parent_rec = this_dir->contents;
      set_723(s_entry1->isorec.volume_sequence_number, volume_sequence_number);
      
      deep_flag = NEED_RE;
      
      if(use_RockRidge) 
	{
	  generate_rock_ridge_attributes(whole_path,
					 short_name, s_entry1,
					 &statbuf, &lstatbuf, deep_flag);
	}
      
      deep_flag = 0;
      
      /* We need to set this temporarily so that the parent to this
	 is correctly determined. */
      s_entry1->filedir = reloc_dir;
      child = find_or_create_directory(reloc_dir, whole_path, 
				       s_entry1, 0);
      if (!no_scandir)
	      scan_directory_tree(child, whole_path, s_entry1);
      s_entry1->filedir = this_dir;
      
      statbuf.st_size = 0;
      statbuf.st_mode &= 0777;
      set_733((char *) s_entry->isorec.size, 0);
      s_entry->size = 0;
      s_entry->isorec.flags[0] = 0;
      s_entry->inode = UNCACHED_INODE;
      s_entry->de_flags |= RELOCATED_DIRECTORY;
      deep_flag = NEED_CL;
    }
  
  if(generate_tables 
     && strcmp(s_entry->name, ".") != 0
     && strcmp(s_entry->name, "..") != 0) 
    {
      char  buffer[2048];
      int nchar;
      switch(lstatbuf.st_mode & S_IFMT)
	{
	case S_IFDIR:
	  sprintf(buffer,"D\t%s\n",
		  s_entry->name);
	  break;

/* extra for WIN32 - if it doesn't have the major/minor defined, then
   S_IFBLK and S_IFCHR type files are unlikely to exist anyway ...
   code similar to that in rock.c */

#if 0
/*
 * Use the device handling code from <device.h>
 */
#ifndef major
#define major(dev) (sizeof(dev_t) <= 2 ? ((dev) >> 8) : \
	(sizeof(dev_t) <= 4 ? (((dev) >> 8) >> 8) : \
	(((dev) >> 16) >> 16)))
#define minor(dev) (sizeof(dev_t) <= 2 ? (dev) & 0xff : \
	(sizeof(dev_t) <= 4 ? (dev) & 0xffff : \
	(dev) & 0xffffffff))
#endif
#endif

#ifdef S_IFBLK
	case S_IFBLK:
	  sprintf(buffer,"B\t%s\t%lu %lu\n",
		  s_entry->name,
		  (unsigned long) major(statbuf.st_rdev),
		  (unsigned long) minor(statbuf.st_rdev));
	  break;
#endif
#ifdef S_IFIFO
	case S_IFIFO:
	  sprintf(buffer,"P\t%s\n",
		  s_entry->name);
	  break;
#endif
#ifdef S_IFCHR
	case S_IFCHR:
	  sprintf(buffer,"C\t%s\t%lu %lu\n",
		  s_entry->name,
		  (unsigned long) major(statbuf.st_rdev),
		  (unsigned long) minor(statbuf.st_rdev));
	  break;
#endif
#ifdef S_IFLNK
	case S_IFLNK:
#ifdef	HAVE_READLINK
	  nchar = readlink(whole_path, 
			   (char *)symlink_buff, 
			   sizeof(symlink_buff));
#else
	  nchar = -1;
#endif
	  symlink_buff[nchar < 0 ? 0 : nchar] = 0;
	  sprintf(buffer,"L\t%s\t%s\n",
		  s_entry->name, symlink_buff);
	  break;
#endif
#ifdef S_IFSOCK
	case S_IFSOCK:
	  sprintf(buffer,"S\t%s\n",
		  s_entry->name);
	  break;
#endif
	case S_IFREG:
	default:
	  sprintf(buffer,"F\t%s\n",
		  s_entry->name);
	  break;
	};
      s_entry->table = strdup(buffer);
    }
  
  if(S_ISDIR(statbuf.st_mode))
    {
      int dflag;
      if (strcmp(short_name,".") != 0 && strcmp(short_name,"..") != 0) 
	{
	  struct directory * child;
	  
	  child = find_or_create_directory(this_dir, whole_path, 
					   s_entry, 1);
	  if (no_scandir) dflag = 1; else
	  dflag = scan_directory_tree(child, whole_path, s_entry);
	  
	  if(!dflag)
	    {
	      lstatbuf.st_mode = (lstatbuf.st_mode & ~S_IFMT) | S_IFREG;
	      if( child->contents == NULL )
		{
		  delete_directory(this_dir, child);
		}
	    }
	}
      /* If unable to scan directory, mark this as a non-directory */
    }
  
  if(use_RockRidge && this_dir == root && strcmp(s_entry->name, ".")  == 0)
    {
      deep_flag |= NEED_CE | NEED_SP;  /* For extension record */
    }
  
  /* Now figure out how much room this file will take in the
     directory */
  
  if(use_RockRidge) 
    {
      generate_rock_ridge_attributes(whole_path,
				     short_name, s_entry,
				     &statbuf, &lstatbuf, deep_flag);
      
    }
  
  return 1;
}


void FDECL2(generate_iso9660_directories, struct directory *, node, FILE*, outfile){
  struct directory * dpnt;

  dpnt = node;

  while (dpnt){
    if( dpnt->extent > session_start )
      {
	generate_one_directory(dpnt, outfile);
      }
    if(dpnt->subdir) generate_iso9660_directories(dpnt->subdir, outfile);
    dpnt = dpnt->next;
  }
}

/*
 * Function:	find_or_create_directory
 *
 * Purpose:	Locate a directory entry in the tree, create if needed.
 *
 * Arguments:	parent & de are never NULL at the same time.
 */
struct directory * FDECL4(find_or_create_directory, struct directory *, parent,
			  const char *, path,
			  struct directory_entry *, de, int, flag)
{
  struct directory		* dpnt;
  struct directory_entry	* orig_de;
  struct directory		* next_brother;
  const char                    * cpnt;
  const char			* pnt;

  orig_de = de;

	/*
	 * XXX It seems that the tree that has been read from the
	 * XXX previous session does not carry whole_name entries.
	 * XXX We provide a hack in multi.c:find_or_create_directory()
	 * XXX that should be removed when a reasonable method could
	 * XXX be found.
	 */
	if (path == NULL) {
		error("Warning: missing whole name for: '%s'\n", de->name);
		path = de->name;
	}
  pnt = strrchr(path, PATH_SEPARATOR);
  if( pnt == NULL )
    {
      pnt = path;
    }
  else
    {
      pnt++;
    }

  if( parent != NULL )
    {
      dpnt = parent->subdir;

      while (dpnt)
	{
	  /*
	   * Weird hack time - if there are two directories by the
	   * same name in the reloc_dir, they are not treated as the
	   * same thing unless the entire path matches completely.
	   */
	  if( flag && strcmp(dpnt->de_name, pnt) == 0 )
	    {
	      return dpnt;
	    }
	  dpnt = dpnt->next;
	}
    }

  /*
   * We don't know if we have a valid directory entry for this one
   * yet.  If not, we need to create one.
   */
  if( de == NULL )
    {
      de = (struct directory_entry *) 
	e_malloc(sizeof (struct directory_entry));
      memset(de, 0, sizeof(struct directory_entry));
      de->next            = parent->contents;
      parent->contents    = de;
      de->name            = strdup(pnt);
      de->whole_name      = strdup(path);
      de->filedir         = parent;
      de->isorec.flags[0] = 2;
      de->priority        = 32768;
      de->inode           = UNCACHED_INODE;
      de->dev             = (dev_t) UNCACHED_DEVICE;
      set_723(de->isorec.volume_sequence_number, volume_sequence_number);
      iso9660_file_length (pnt, de, 1);

      init_fstatbuf();
      /*
       * It doesn't exist for real, so we cannot add any Rock Ridge.
       */
      if(use_RockRidge)
	{
	  fstatbuf.st_mode = 0555 | S_IFDIR;
	  fstatbuf.st_nlink = 2;
	  generate_rock_ridge_attributes("",
					 (char *) pnt, de,
					 &fstatbuf, 
					 &fstatbuf, 0);
	}
      iso9660_date(de->isorec.date, fstatbuf.st_mtime);

    }

  /*
   * If we don't have a directory for this one yet, then allocate it
   * now, and patch it into the tree in the appropriate place.
   */
  dpnt             = (struct directory *) e_malloc(sizeof(struct directory));
  memset(dpnt, 0, sizeof(struct directory));
  dpnt->next       = NULL;
  dpnt->subdir     = NULL;
  dpnt->self       = de;
  dpnt->contents   = NULL;
  dpnt->whole_name = strdup(path);
  cpnt             = strrchr(path, PATH_SEPARATOR);
  if(cpnt)
    cpnt++;
  else
    cpnt = path;
  dpnt->de_name    = strdup(cpnt);
  dpnt->size       = 0;
  dpnt->extent     = 0;
  dpnt->jextent    = 0;
  dpnt->jsize      = 0;

  if( orig_de == NULL )
    {
      struct stat xstatbuf;
      int sts;

      /*
       * Now add a . and .. entry in the directory itself.
       * This is a little tricky - if the real directory
       * exists, we need to stat it first.  Otherwise, we
       * use the fictitious fstatbuf which points to the time
       * at which mkisofs was started.
       */
      sts = stat_filter(parent->whole_name, &xstatbuf);
      if( sts == 0 )
	{
	  attach_dot_entries(dpnt, &xstatbuf);
	}
      else
	{
	  attach_dot_entries(dpnt, &fstatbuf);
	}
    }

  if(!parent || parent == root)
    {
      if (!root) 
	{
	  root = dpnt;  /* First time through for root directory only */
	  root->depth = 0;
	  root->parent = root;
	} else {
	  dpnt->depth = 1;
	  if(!root->subdir)
	    {
	      root->subdir = dpnt;
	    }
	  else 
	    {
	      next_brother = root->subdir;
	      while(next_brother->next) next_brother = next_brother->next;
	      next_brother->next = dpnt;
	    }
	  dpnt->parent = parent;
	}
    } 
  else 
    {
      /* Come through here for  normal traversal of  tree */
#ifdef DEBUG
      fprintf(stderr,"%s(%d) ", path, dpnt->depth);
#endif
      if(parent->depth >  RR_relocation_depth) 
	{
#ifdef	USE_LIBSCHILY
	  comerrno(EX_BAD, "Directories too deep for '%s' (%d) max is %d.\n",
				path, parent->depth, RR_relocation_depth);
#else
	  fprintf(stderr,"Directories too deep for '%s' (%d) max is %d.\n",
				path, parent->depth, RR_relocation_depth);
	  exit(1);
#endif
	}
      
      dpnt->parent = parent; 
      dpnt->depth = parent->depth + 1;
      
      if(!parent->subdir)
	{
	  parent->subdir = dpnt;
	}
      else 
	{
	  next_brother = parent->subdir;
	  while(next_brother->next) next_brother = next_brother->next;
	  next_brother->next = dpnt;
	}
    }

  return dpnt;
}

/*
 * Function:	delete_directory
 *
 * Purpose:	Locate a directory entry in the tree, create if needed.
 *
 * Arguments:
 */
static void FDECL2(delete_directory, struct directory *, parent, struct directory *, child)
{
  struct directory		* tdir;

  if( child->contents != NULL )
    {
#ifdef	USE_LIBSCHILY
      comerrno(EX_BAD, "Unable to delete non-empty directory\n");
#else
      fprintf(stderr, "Unable to delete non-empty directory\n");
      exit(1);
#endif
    }

  free(child->whole_name);
  child->whole_name = NULL;

  free(child->de_name);
  child->de_name = NULL;

  if( parent->subdir == child )
    {
      parent->subdir = child->next;
    }
  else
    {
      for( tdir = parent->subdir; tdir->next != NULL; tdir = tdir->next )
	{
	  if( tdir->next == child )
	    {
	      tdir->next = child->next;
	      break;
	    }
	}
      if( tdir == NULL )
	{
#ifdef	USE_LIBSCHILY
	  comerrno(EX_BAD, "Unable to locate child directory in parent list\n");
#else
	  fprintf(stderr, "Unable to locate child directory in parent list\n");
	  exit(1);
#endif
	}
    }
  free(child);
  return;
}

int FDECL1(sort_tree, struct directory *, node){
  struct directory * dpnt;
  int ret = 0;

  dpnt = node;

  while (dpnt){
    ret = sort_n_finish(dpnt);
    if( ret )
      {
	break;
      }
    
    if(dpnt->subdir) sort_tree(dpnt->subdir);
    dpnt = dpnt->next;
  }
  return ret;
}

void FDECL1(dump_tree, struct directory *, node){
  struct directory * dpnt;

  dpnt = node;

  while (dpnt){
    fprintf(stderr,"%4d %5d %s\n",dpnt->extent, dpnt->size, dpnt->de_name);
    if(dpnt->subdir) dump_tree(dpnt->subdir);
    dpnt = dpnt->next;
  }
}

void FDECL1(update_nlink_field, struct directory *, node)
{
    struct directory		* dpnt;
    struct directory		* xpnt;
    struct directory_entry	* s_entry;
    int				  i;

    dpnt = node;
    
    while (dpnt)
    {
	if (dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) {
	    dpnt = dpnt->next;
	    continue;
	}

	/*
	 * First, count up the number of subdirectories this guy has.
	 */
        for(i=0, xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next)
            if ((xpnt->dir_flags & INHIBIT_ISO9660_ENTRY) == 0)
                i++;
	/*
	 * Next check to see if we have any relocated directories
	 * in this directory.   The nlink field will include these
	 * as real directories when they are properly relocated.
	 *
	 * In the non-rockridge disk, the relocated entries appear
	 * as zero length files.
	 */
	for(s_entry = dpnt->contents; s_entry; s_entry = s_entry->next)
	{
		if( (s_entry->de_flags  & RELOCATED_DIRECTORY) != 0 &&
			(s_entry->de_flags  & INHIBIT_ISO9660_ENTRY) == 0)
		{
			i++;
		}
	}
	/*
	 * Now update the field in the Rock Ridge entry.
	 */
	update_nlink(dpnt->self, i + 2);

	/*
	 * Update the '.' entry for this directory.
	 */
	update_nlink(dpnt->contents, i + 2);

	/*
	 * Update all of the '..' entries that point to this guy.
	 */
	for(xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next)
	    update_nlink(xpnt->contents->next, i + 2);

	if(dpnt->subdir) update_nlink_field(dpnt->subdir);
	dpnt = dpnt->next;
    }
}

/*
 * something quick and dirty to locate a file given a path
 * recursively walks down path in filename until it finds the
 * directory entry for the desired file 
 */
struct directory_entry * FDECL2(search_tree_file, struct directory *, 
				node,char *, filename)
{
  struct directory_entry * depnt;
  struct directory	 * dpnt;
  char			 * p1;
  char			 * rest;
  char			 * subdir;

  /*
   * strip off next directory name from filename 
   */
  subdir = strdup(filename);

  if( (p1=strchr(subdir, '/')) == subdir )
    {
      fprintf(stderr,"call to search_tree_file with an absolute path, stripping\n");
      fprintf(stderr,"initial path separator. Hope this was intended...\n");
      memmove(subdir, subdir+1, strlen(subdir)-1);
      p1 = strchr(subdir, '/');
    }

  /*
   * do we need to find a subdirectory 
   */
  if (p1) 
    {
      *p1 = '\0';

#ifdef DEBUG_TORITO
      fprintf(stderr,"Looking for subdir called %s\n",p1); 
#endif

      rest = p1+1;

#ifdef DEBUG_TORITO
      fprintf(stderr,"Remainder of path name is now %s\n", rest); 
#endif
      
      dpnt = node->subdir;
     while( dpnt )
       {
#ifdef DEBUG_TORITO
	 fprintf(stderr,"%4d %5d %s\n", dpnt->extent, dpnt->size, 
		 dpnt->de_name); 
#endif
	 if (!strcmp(subdir, dpnt->de_name)) 
	   {
#ifdef DEBUG_TORITO
	     fprintf(stderr,"Calling next level with filename = %s", rest); 
#endif
	     return(search_tree_file( dpnt, rest ));
	   }
	 dpnt = dpnt->next;
       }
      
     /* if we got here means we couldnt find the subdir */
     return (NULL);
    }    
  else 
    {
      /* 
       * look for a normal file now 
       */
      depnt = node->contents;
      while (depnt)
	{
#ifdef DEBUG_TORITO
	  fprintf(stderr,"%4d %5d %s\n",depnt->isorec.extent, 
		  depnt->size, depnt->name); 
#endif
	  if (!strcmp(filename, depnt->name)) 
	    {
#ifdef DEBUG_TORITO
	      fprintf(stderr,"Found our file %s", filename); 
#endif
	      return(depnt);
	    }
	  depnt = depnt->next;
	}
      /* 
       * if we got here means we couldnt find the subdir 
       */
      return (NULL);
    }
#ifdef	ERIC_FUN
  fprintf(stderr,"We cant get here in search_tree_file :-/ \n");
#endif
}

void init_fstatbuf()
{
  time_t		    current_time;

  if(fstatbuf.st_ctime == 0)
    {
      time (&current_time);
      if( rationalize )
	{
	  fstatbuf.st_uid = 0;
	  fstatbuf.st_gid = 0;
	}
      else
	{
	  fstatbuf.st_uid = getuid();
	  fstatbuf.st_gid = getgid();
	}
      fstatbuf.st_ctime = current_time;
      fstatbuf.st_mtime = current_time;
      fstatbuf.st_atime = current_time;
    }
}
