

/* Adapter to libisoburn, libisofs and libburn for xorriso,
   a command line oriented batch and dialog tool which creates, loads,
   manipulates and burns ISO 9660 filesystem images.

   Copyright 2007-2008 Thomas Schmitt, <scdbackup@gmx.net>

   Provided under GPL version 2.
*/

#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>



/* ------------------------------------------------------------------------ */

#ifndef Xorriso_standalonE

/* The library which does the ISO 9660 / RockRidge manipulations */
#include <libisofs/libisofs.h>

/* The library which does MMC optical drive operations */
#include <libburn/libburn.h>

/* The library which enhances overwriteable media with ISO 9660 multi-session
   capabilities via the method invented by Andy Polyakov for growisofs */
#include <libisoburn/libisoburn.h>

/* The official xorriso options API. "No shortcuts" */
#include "xorriso.h"

/* The inner description of XorrisO */
#include "xorriso_private.h"

/* The inner isofs- and burn-library interface */
#include "xorrisoburn.h"

#else /* ! Xorriso_standalonE */

#include "../libisofs/libisofs.h"
#include "../libburn/libburn.h"
#include "../libisoburn/libisoburn.h"
#include "xorriso.h"
#include "xorriso_private.h"
#include "xorrisoburn.h"

#endif /* Xorriso_standalonE */

/* Some private in advance declarations */
int  Xorriso_pacifier_loop(struct XorrisO *xorriso, struct burn_drive *drive,
                          int flag);

int Xorriso__read_pacifier(IsoImage *image, IsoFileSource *filesource);

int Xorriso_tree_graft_node(struct XorrisO *xorriso, IsoImage *volume,
                            IsoDir *dir, char *disk_path, char *img_name,
                            char *nominal_source, char *nominal_target,
                            off_t offset, off_t cut_size,
                            IsoNode **node, int flag);

int Xorriso_is_split(struct XorrisO *xorriso, IsoNode *node, int flag);


#define LIBISO_ISDIR(node) (iso_node_get_type(node) == LIBISO_DIR)
#define LIBISO_ISREG(node) (iso_node_get_type(node) == LIBISO_FILE)
#define LIBISO_ISLNK(node) (iso_node_get_type(node) == LIBISO_SYMLINK)
#define LIBISO_ISCHR(node) (iso_node_get_type(node) == LIBISO_SPECIAL && \
                            S_ISCHR(iso_node_get_mode(node)))
#define LIBISO_ISBLK(node) (iso_node_get_type(node) == LIBISO_SPECIAL && \
                            S_ISBLK(iso_node_get_mode(node)))
#define LIBISO_ISFIFO(node) (iso_node_get_type(node) == LIBISO_SPECIAL && \
                             S_ISFIFO(iso_node_get_mode(node)))
#define LIBISO_ISSOCK(node) (iso_node_get_type(node) == LIBISO_SPECIAL && \
                             S_ISSOCK(iso_node_get_mode(node)))

/* CD specs say one shall not write tracks < 600 kiB */
#define Xorriso_cd_min_track_sizE 300


/* ------------------------------------------------------------------------ */


int Xorriso_startup_libraries(struct XorrisO *xorriso, int flag)
{
 int ret, major, minor, micro;
 char *handler_prefix= NULL;
 char *queue_sev, *print_sev, reason[1024];


/* First an ugly compile time check for header version compatibility.
   If everthing matches, then no C code is produced. In case of mismatch,
   intentionally faulty C code will be inserted.
*/

/* The minimum requirement of xorriso towards the libisoburn header
   at compile time is defined in xorriso/xorrisoburn.h 
     xorriso_libisoburn_req_major
     xorriso_libisoburn_req_minor
     xorriso_libisoburn_req_micro
   It gets compared against the version macros in libburn/libburn.h :
     isoburn_header_version_major
     isoburn_header_version_minor
     isoburn_header_version_micro
   If the header is too old then the following code shall cause failure of
   cdrskin compilation rather than to allow production of a program with
   unpredictable bugs or memory corruption.
   The compiler messages supposed to appear in this case are:
      error: 'LIBISOBURN_MISCONFIGURATION' undeclared (first use in this function)
      error: 'INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libisoburn_dot_h_TOO_OLD__SEE_xorrisoburn_dot_c' undeclared (first use in this function)
      error: 'LIBISOBURN_MISCONFIGURATION_' undeclared (first use in this function)
*/
/* The indendation is an advise of man gcc to help old compilers ignoring */
 #if xorriso_libisoburn_req_major > isoburn_header_version_major
 #define Isoburn_libisoburn_dot_h_too_olD 1
 #endif
 #if xorriso_libisoburn_req_major == isoburn_header_version_major && xorriso_libisoburn_req_minor > isoburn_header_version_minor
 #define Isoburn_libisoburn_dot_h_too_olD 1
 #endif
 #if xorriso_libisoburn_req_minor == isoburn_header_version_minor && xorriso_libisoburn_req_micro > isoburn_header_version_micro
 #define Isoburn_libisoburn_dot_h_too_olD 1
 #endif

#ifdef Isoburn_libisoburn_dot_h_too_olD
LIBISOBURN_MISCONFIGURATION = 0;
INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libisoburn_dot_h_TOO_OLD__SEE_xorrisoburn_dot_c = 0;
LIBISOBURN_MISCONFIGURATION_ = 0;
#endif

/* End of ugly compile time test (scroll up for explanation) */


 sprintf(xorriso->info_text, "Starting up libraries ...\n");
 Xorriso_info(xorriso, 0);

 handler_prefix= calloc(strlen(xorriso->progname)+3+1, 1);
 if(handler_prefix==NULL) {
   sprintf(xorriso->info_text,
           "Cannot allocate memory for initializing libraries");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
 reason[0]= 0;
 ret= isoburn_initialize(reason, 0);
 if(ret==0) {
   sprintf(xorriso->info_text, "Cannot initialize libraries");
   if(reason[0])
     sprintf(xorriso->info_text+strlen(xorriso->info_text),
             ". Reason given:\n%s", reason);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   free(handler_prefix);
   return(0);
 }
 ret= isoburn_is_compatible(isoburn_header_version_major,
                            isoburn_header_version_minor,
                            isoburn_header_version_micro, 0);
 if(ret<=0) {
   isoburn_version(&major, &minor, &micro);
   sprintf(xorriso->info_text,
          "libisoburn version too old: %d.%d.%d . Need at least: %d.%d.%d .\n",
          major, minor, micro,
          isoburn_header_version_major, isoburn_header_version_minor,
          isoburn_header_version_micro);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }

 xorriso->libs_are_started= 1;

 queue_sev= "ALL";
 if(xorriso->library_msg_direct_print) {

   /* >>> need option for controlling this in XorrisO.
          See also Xorriso_msgs_submit */;

   print_sev= xorriso->report_about_text;
 } else
   print_sev= "NEVER";

 iso_set_msgs_severities(queue_sev, print_sev, "libsofs : ");
 burn_msgs_set_severities(queue_sev, print_sev, "libburn : ");

 /* ??? >>> do we want united queues ? */
 /* burn_set_messenger(iso_get_messenger()); */

 sprintf(handler_prefix, "%s : ", xorriso->progname);
 burn_set_signal_handling(handler_prefix, NULL, 0);
 
 Xorriso_process_msg_queues(xorriso,0);
 if(reason[0]) {
   sprintf(xorriso->info_text, "%s", reason);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 }

 sprintf(xorriso->info_text, "Library startup done.\n");
 Xorriso_info(xorriso, 0);
 free(handler_prefix);
 return(1);
}


/* @param flag bit0= global shutdown of libraries */
int Xorriso_detach_libraries(struct XorrisO *xorriso, int flag)
{
 Xorriso_give_up_drive(xorriso, 3);
 if(xorriso->in_volset_handle!=NULL) { /* standalone image */
   iso_image_unref((IsoImage *) xorriso->in_volset_handle);
   xorriso->in_volset_handle= NULL;
 }
 if(flag&1) {
   if(xorriso->libs_are_started==0)
     return(0);
   isoburn_finish();
 }
 return(1);
}


/* @param flag bit1= obtain outdrive, else indrive */
int Xorriso_get_drive_handles(struct XorrisO *xorriso,
                              struct burn_drive_info **dinfo,
                              struct burn_drive **drive,
                              char *attempt, int flag)
{
 if(flag&2)
   *dinfo= (struct burn_drive_info *) xorriso->out_drive_handle;
 else
   *dinfo= (struct burn_drive_info *) xorriso->in_drive_handle;
 if(*dinfo==NULL) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text, "No %s drive aquired %s",
           (flag&2 ? "output" : "input"), attempt);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }
 *drive= (*dinfo)[0].drive;
 return((*drive)!=NULL);
}


/* ts A80408 : Moved to libburn-0.4.4
*/
#define Xorriso_on_libburn_after_0_4_2 yes


#ifndef Xorriso_on_libburn_after_0_4_2

/* >>> todo: throw out the copies of libdax_msgs entrails */

/* <<< to be replaced by libburn-0.4.3 API call burn_sev_to_text().
   This is a copy of libdax_msgs__sev_to_text() which is not exposed
   by the API of of libburn-0.4.2 . As soon as xorriso gets based on
   libburn-0.4.4 this redundancy is to be removed.
   It is safe, nevertheless, because the severity codes are eternal.
*/
#define LIBDAX_MSGS_SEV_ALL                                          0x00000000
#define LIBDAX_MSGS_SEV_ERRFILE                                      0x08000000
#define LIBDAX_MSGS_SEV_DEBUG                                        0x10000000
#define LIBDAX_MSGS_SEV_UPDATE                                       0x20000000
#define LIBDAX_MSGS_SEV_NOTE                                         0x30000000
#define LIBDAX_MSGS_SEV_HINT                                         0x40000000
#define LIBDAX_MSGS_SEV_WARNING                                      0x50000000
#define LIBDAX_MSGS_SEV_SORRY                                        0x60000000
#define LIBDAX_MSGS_SEV_MISHAP                                       0x64000000
#define LIBDAX_MSGS_SEV_FAILURE                                      0x68000000
#define LIBDAX_MSGS_SEV_FATAL                                        0x70000000
#define LIBDAX_MSGS_SEV_ABORT                                        0x71000000
#define LIBDAX_MSGS_SEV_NEVER                                        0x7fffffff

#endif /* ! Xorriso_on_libburn_after_0_4_2 */


int Xorriso__sev_to_text(int severity, char **severity_name,
                         int flag)
{
 int ret;

  ret= iso_sev_to_text(severity, severity_name);
  if(ret>0)
    return(ret);

#ifdef Xorriso_on_libburn_after_0_4_2

  ret= burn_sev_to_text(severity, severity_name, 0);
  if(ret>0)
    return(ret);
 *severity_name= "";
 return(0);

#else /* Xorriso_on_libburn_after_0_4_2 */

 if(flag&1) {
   *severity_name= "NEVER\nABORT\nFATAL\nFAILURE\nSORRY\nWARNING\nHINT\nNOTE\nUPDATE\nDEBUG\nALL";
   return(1);
 }
 *severity_name= "";
 if(severity>=LIBDAX_MSGS_SEV_NEVER)
   *severity_name= "NEVER";
 else if(severity>=LIBDAX_MSGS_SEV_ABORT)
   *severity_name= "ABORT";
 else if(severity>=LIBDAX_MSGS_SEV_FATAL)
   *severity_name= "FATAL";
 else if(severity>=LIBDAX_MSGS_SEV_FAILURE)
   *severity_name= "FAILURE";
 else if(severity>=LIBDAX_MSGS_SEV_MISHAP)
   *severity_name= "MISHAP";
 else if(severity>=LIBDAX_MSGS_SEV_SORRY)
   *severity_name= "SORRY";
 else if(severity>=LIBDAX_MSGS_SEV_WARNING)
   *severity_name= "WARNING";
 else if(severity>=LIBDAX_MSGS_SEV_HINT)
   *severity_name= "HINT";
 else if(severity>=LIBDAX_MSGS_SEV_NOTE)
   *severity_name= "NOTE";
 else if(severity>=LIBDAX_MSGS_SEV_UPDATE)
   *severity_name= "UPDATE";
 else if(severity>=LIBDAX_MSGS_SEV_DEBUG)
   *severity_name= "DEBUG";
 else if(severity>=LIBDAX_MSGS_SEV_ERRFILE)
   *severity_name= "ERRFILE";
 else if(severity>=LIBDAX_MSGS_SEV_ALL)
   *severity_name= "ALL";
 else {
   *severity_name= "";
   return(0);
 }
 return(1);

#endif /* ! Xorriso_on_libburn_after_0_4_2 */

}


int Xorriso__text_to_sev(char *severity_name, int *severity_number, int flag)
{
 int ret= 1;

 ret= iso_text_to_sev(severity_name, severity_number);
 if(ret>0)
   return(ret);

#ifndef Xorriso_on_libburn_after_0_4_2
 if(severity_name[0]==0)
   *severity_number= 0;
 else if(strcmp(severity_name, "MISHAP")==0)
   *severity_number= LIBDAX_MSGS_SEV_MISHAP;
 else if(strcmp(severity_name, "ERRFILE")==0)
   *severity_number= LIBDAX_MSGS_SEV_ERRFILE;
 else
#endif /* ! Xorriso_on_libburn_after_0_4_2 */

   ret= burn_text_to_sev(severity_name, severity_number, 0);
 return(ret);
}


/* @param flag bit0= report libisofs error text
               bit1= victim is disk_path
               bit2= do not inquire libisofs, report msg_text and min_severity
*/
int Xorriso_report_iso_error(struct XorrisO *xorriso, char *victim,
                        int iso_error_code, char msg_text[], int os_errno,
                        char min_severity[], int flag)
{
 int error_code, iso_sev, min_sev, ret;
 char *sev_text_pt, *msg_text_pt= NULL;
 char sfe[6*SfileadrL];
 static int sorry_sev= -1;

 if(sorry_sev<0)
   Xorriso__text_to_sev("SORRY", &sorry_sev, 0);

 if(flag&4) {
   error_code= 0x00050000;
   Xorriso__text_to_sev(min_severity, &iso_sev, 0);
 } else {
   error_code= iso_error_get_code(iso_error_code);
   if(error_code < 0x00030000 || error_code >= 0x00040000)
     error_code= (error_code & 0xffff) | 0x00050000;
   if(flag&1)
     msg_text_pt= (char *) iso_error_to_msg(iso_error_code);
   iso_sev= iso_error_get_severity(iso_error_code);
 }
 if(msg_text_pt==NULL)
   msg_text_pt= msg_text;

 if(iso_sev >= sorry_sev && (flag & 2) && victim[0])
   Xorriso_msgs_submit(xorriso, 0, victim, 0, "ERRFILE", 0);
 sev_text_pt= min_severity;
 Xorriso__text_to_sev(min_severity, &min_sev, 0);
 if(min_sev < iso_sev && !(flag&4))
   Xorriso__sev_to_text(iso_sev, &sev_text_pt, 0);
 strcpy(sfe, msg_text_pt);
 if(victim[0]) {
   strcat(sfe, ": ");
   Text_shellsafe(victim, sfe+strlen(sfe), 0);
 }
 ret= Xorriso_msgs_submit(xorriso, error_code, sfe, os_errno, sev_text_pt, 4);
 return(ret);
}


/* @param flag bit0= suppress messages below UPDATE
               bit1= suppress messages below FAILURE
*/
int Xorriso_set_image_severities(struct XorrisO *xorriso, int flag)
{
 char *queue_sev, *print_sev;

 if(flag&2)
   queue_sev= "FAILURE";
 else if(flag&1)
   queue_sev= "UPDATE";
 else
   queue_sev= "ALL";
 if(xorriso->library_msg_direct_print)
   print_sev= xorriso->report_about_text;
 else
   print_sev= "NEVER";
 iso_set_msgs_severities(queue_sev, print_sev, "libisofs : ");
 return(1);
}


int Xorriso_update_volid(struct XorrisO *xorriso, int flag)
{
 int gret, sret= 1;

 gret= Xorriso_get_volid(xorriso, xorriso->loaded_volid, 0);
 if(gret<=0 || (!xorriso->volid_default) || xorriso->loaded_volid[0]==0)
   sret= Xorriso_set_volid(xorriso, xorriso->volid, 1);
 return(gret>0 && sret>0);
} 
 

int Xorriso_create_empty_iso(struct XorrisO *xorriso, int flag)
{
 int ret;
 IsoImage *volset;
 struct isoburn_read_opts *ropts;
 struct burn_drive_info *dinfo= NULL;
 struct burn_drive *drive= NULL;

 if(xorriso->out_drive_handle != NULL) {
   ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                  "on attempt to attach volset to drive", 2);
   if(ret<=0)
     return(ret);
 }
 if(xorriso->in_volset_handle!=NULL) {
   iso_image_unref((IsoImage *) xorriso->in_volset_handle);
   xorriso->in_volset_handle= NULL;
   xorriso->loaded_volid[0]= 0;
   xorriso->volset_change_pending= 0;
   xorriso->no_volset_present= 0;
 }

 ret= isoburn_ropt_new(&ropts, 0);
 if(ret<=0)
   return(ret);
 /* Note: no return before isoburn_ropt_destroy() */
 isoburn_ropt_set_extensions(ropts, isoburn_ropt_pretend_blank);
 isoburn_ropt_set_input_charset(ropts, NULL);
 isoburn_set_read_pacifier(drive, NULL, NULL);
 ret= isoburn_read_image(drive, ropts, &volset);
 Xorriso_process_msg_queues(xorriso,0);
 isoburn_ropt_destroy(&ropts, 0);
 if(ret<=0) {
   sprintf(xorriso->info_text, "Failed to create new empty ISO image object");
   Xorriso_report_iso_error(xorriso, "", ret, xorriso->info_text, 0, "FATAL",
                            0);
   return(-1);
 }
 xorriso->in_volset_handle= (void *) volset;
 Xorriso_update_volid(xorriso, 0);
 xorriso->volset_change_pending= 0;
 xorriso->no_volset_present= 0;
 return(1);
}


/* @param flag bit0= aquire as isoburn input drive
               bit1= aquire as libburn output drive (as isoburn drive if bit0)
               bit2= regard overwriteable media as blank
               bit3= if the drive is a regular disk file: truncate it to
                     the write start address
   @return <=0 failure , 1= ok
           2=success, but not writeable with bit1
           3=success, but not blank and not ISO with bit0
*/
int Xorriso_aquire_drive(struct XorrisO *xorriso, char *adr, int flag)
{
 int ret, hret, not_writeable= 0, has_what;
 uint32_t size;
 struct burn_drive_info *dinfo= NULL, *out_dinfo, *in_dinfo;
 struct burn_drive *drive, *out_drive, *in_drive;
 enum burn_disc_status state;
 IsoImage *volset = NULL;
 struct isoburn_read_opts *ropts= NULL;
 char adr_data[SfileadrL], *libburn_adr, *boot_fate;

 if((flag&3)==0) { 
   sprintf(xorriso->info_text, 
         "XORRISOBURN program error : Xorriso_aquire_drive bit0+bit1 not set");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
 ret= Xorriso_give_up_drive(xorriso, (flag&3)|8);
 if(ret<=0)
   return(ret);

 libburn_adr= adr;
 if(strcmp(adr,"stdio:/dev/fd/1")==0) {
   if(xorriso->dev_fd_1<0) {
     sprintf(xorriso->info_text,
     "-*dev \"stdio:/dev/fd/1\" was not a start argument. Cannot use it now.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     {ret= 0; goto ex;}
   } else {
     sprintf(adr_data, "stdio:/dev/fd/%d", xorriso->dev_fd_1);
     libburn_adr= adr_data;
   }
 }

 if((flag&3)==1 && xorriso->out_drive_handle!=NULL) {
   ret= Xorriso_get_drive_handles(xorriso, &out_dinfo, &out_drive,
                         "on attempt to compare new indev with outdev", 2);
   if(ret<=0)
     goto ex;
   ret= burn_drive_equals_adr(out_drive, libburn_adr, 1);
   if(ret==1)
     dinfo= out_dinfo;
 } else if((flag&3)==2 && xorriso->in_drive_handle!=NULL) {
   ret= Xorriso_get_drive_handles(xorriso, &in_dinfo, &in_drive,
                         "on attempt to compare new indev with outdev", 0);
   if(ret<=0)
     goto ex;
   ret= burn_drive_equals_adr(in_drive, libburn_adr, 1);
   if(ret==1)
     dinfo= in_dinfo;
 }

 if(dinfo==NULL) {
   ret= isoburn_drive_aquire(&dinfo, libburn_adr, 1|((flag&(8|4))>>1));
   Xorriso_process_msg_queues(xorriso,0);
   if(ret<=0) {
     sprintf(xorriso->info_text,"Cannot aquire drive '%s'", adr); 
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }
 }
 drive= dinfo[0].drive;
 state= isoburn_disc_get_status(drive);
 Xorriso_process_msg_queues(xorriso,0);
 if(flag&1) {
   volset= isoburn_get_attached_image(drive);
   if(volset != NULL) { /* The image object is already created */
     iso_image_unref(volset);
   }
 }

 if(flag&2) {
   xorriso->out_drive_handle= dinfo;
   if(Sfile_str(xorriso->outdev, adr, 0)<=0)
     {ret= -1; goto ex;}
   if(state != BURN_DISC_BLANK && state != BURN_DISC_APPENDABLE) {
     sprintf(xorriso->info_text, "Disc status unsuitable for writing");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0);
     not_writeable= 1;
   }
 }
 if(flag&1) {
   xorriso->in_drive_handle= dinfo;
   if(Sfile_str(xorriso->indev, adr, 0)<=0)
     {ret= -1; goto ex;}
 } else if(flag&2) {
   if(xorriso->in_volset_handle==NULL) {
     /* No volume loaded: create empty one */
     ret= Xorriso_create_empty_iso(xorriso, 0);
     if(ret<=0)
       goto ex;
   } else {
     iso_image_ref((IsoImage *) xorriso->in_volset_handle);
     ret= isoburn_attach_image(drive, (IsoImage *) xorriso->in_volset_handle);
     if(ret<=0) {
       sprintf(xorriso->info_text,
               "Failed to attach ISO image object to outdev");
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
       {ret= -1; goto ex;}
     }
   }
   Xorriso_toc(xorriso, 1|2);
   {ret= 1+not_writeable; goto ex;}
 }

 if(xorriso->in_volset_handle!=NULL)
   iso_image_unref((IsoImage *) xorriso->in_volset_handle);
 xorriso->in_volset_handle= NULL;

 /* check for invalid state */
 if(state != BURN_DISC_BLANK && state != BURN_DISC_APPENDABLE &&
    state != BURN_DISC_FULL) {
   sprintf(xorriso->info_text,
           "Disc status not blank and unsuitable for reading");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0);
   {ret= 0; goto ex;}
 }
 /* fill read opts */ 
 ret= isoburn_ropt_new(&ropts, 0);
 if(ret<=0)
   goto ex;
 isoburn_ropt_set_extensions(ropts, isoburn_ropt_noiso1999);
 isoburn_ropt_set_default_perms(ropts, (uid_t) 0, (gid_t) 0, (mode_t) 0555);
 isoburn_ropt_set_input_charset(ropts, NULL);
 
 Xorriso_set_image_severities(xorriso, 1); /* No DEBUG messages */
 Xorriso_pacifier_reset(xorriso, 0);
 isoburn_set_read_pacifier(drive, Xorriso__read_pacifier, (void *) xorriso);
 if(isoburn_read_image(drive, ropts, &volset) <= 0) {
   Xorriso_process_msg_queues(xorriso,0);
   Xorriso_set_image_severities(xorriso, 0);
   sprintf(xorriso->info_text,"Cannot read ISO image volset");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 3; goto ex;
 }
 Xorriso_pacifier_callback(xorriso, "nodes read", xorriso->pacifier_count, 0,
                           "", 1); /* report end count */
 xorriso->in_volset_handle= (void *) volset;
 Xorriso_set_image_severities(xorriso, 0);
 Xorriso_update_volid(xorriso, 0);

 if(xorriso->out_drive_handle != NULL &&
    xorriso->out_drive_handle != xorriso->in_drive_handle) {
   ret= Xorriso_get_drive_handles(xorriso, &out_dinfo, &out_drive,
                         "on attempt to attach ISO image volset to outdev", 2);
   if(ret<=0)
     goto ex;
   iso_image_ref((IsoImage *) xorriso->in_volset_handle);
   isoburn_attach_image(out_drive, xorriso->in_volset_handle);
 }
 Xorriso_process_msg_queues(xorriso,0);
 isoburn_ropt_get_size_what(ropts, &size, &has_what);
 if(has_what & isoburn_ropt_has_el_torito) {
   if(xorriso->patch_isolinux_image)
     boot_fate= "patched as isolinux image";
   else if(xorriso->keep_boot_image)
     boot_fate= "kept unchanged";
   else
     boot_fate= "discarded";
   sprintf(xorriso->info_text,
         "Detected El-Torito boot information which currently is set to be %s",
         boot_fate);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); 
 }

 Xorriso_toc(xorriso, 1);
 if(xorriso->loaded_volid[0]!=0) {
   sprintf(xorriso->result_line,"Volume id    : '%s'\n",xorriso->loaded_volid);
   Xorriso_result(xorriso,0);
   if(strcmp(xorriso->loaded_volid, xorriso->volid)!=0 &&
      !xorriso->volid_default) {
     sprintf(xorriso->result_line, "New volume id: '%s'\n", xorriso->volid);
     Xorriso_result(xorriso,0);
   }
 }
 ret= 1+not_writeable;
ex:
 Xorriso_process_msg_queues(xorriso,0);
 if(ret<=0) {
   hret= Xorriso_give_up_drive(xorriso, flag&3);
   if(hret<ret)
     ret= hret;
 }
 if(ropts!=NULL)
   isoburn_ropt_destroy(&ropts, 0);
 return(ret);
}


/* @param flag bit0=input drive
               bit1=output drive
               bit2=eject
               bit3=no info message or toc
*/
int Xorriso_give_up_drive(struct XorrisO *xorriso, int flag)
{
 int in_is_out_too, ret;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 char sfe[5*SfileadrL];
 
 in_is_out_too= (xorriso->in_drive_handle == xorriso->out_drive_handle);
 if((flag&4) && in_is_out_too && (flag&(1|2))) {
   if((flag&3)!=3) {
     sprintf(xorriso->info_text,"Giving up for -eject whole -dev %s",
               Text_shellsafe(xorriso->indev, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   }
   flag|= 3; /* give up in/out drive to eject it */
 }
   
 if((flag&1) && xorriso->in_drive_handle != NULL) {
   Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                             "on attempt to give up drive", 0);

   if(!in_is_out_too) {
     if(drive!=NULL && !in_is_out_too)
       isoburn_drive_release(drive,!!(flag&4));
     if(dinfo!=NULL && !in_is_out_too)
       burn_drive_info_free(dinfo);
   }
   xorriso->in_drive_handle= NULL;
   xorriso->indev[0]= 0;

   if(xorriso->in_volset_handle!=NULL)
     iso_image_unref((IsoImage *) xorriso->in_volset_handle);
   xorriso->in_volset_handle= NULL;
   xorriso->loaded_volid[0]= 0;
   xorriso->volset_change_pending= 0;
   xorriso->no_volset_present= 0; 
   in_is_out_too= 0;
 }
 if((flag&2) && xorriso->out_drive_handle!=NULL) {
   Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                             "on attempt to give up drive", 2);
   if(!in_is_out_too) {
     if(drive!=NULL)
       isoburn_drive_release(drive,!!(flag&4));
     if(dinfo!=NULL)
       burn_drive_info_free(dinfo);
   }
   xorriso->out_drive_handle= NULL;
   xorriso->outdev[0]= 0;
 } else if((flag&1) && xorriso->out_drive_handle!=NULL) {
   ret= Xorriso_create_empty_iso(xorriso, 0);
   if(ret<=0)
     return(ret);
   if(!(flag&8)) {
     sprintf(xorriso->info_text,
             "Only the output drive remains. Created empty ISO image.\n");
     Xorriso_info(xorriso, 0);
     Xorriso_toc(xorriso, 1|2);
   }
 }
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


int Xorriso_make_write_options(
        struct XorrisO *xorriso, struct burn_drive *drive,
        struct burn_write_opts **burn_options, int flag)
{
 int drive_role;

 *burn_options= burn_write_opts_new(drive);
 if(*burn_options==NULL) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,"Cannot allocate option set");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }
 burn_write_opts_set_simulate(*burn_options, !!xorriso->do_dummy);
 drive_role= burn_drive_get_drive_role(drive);
 burn_write_opts_set_multi(*burn_options,
                       !(xorriso->do_close || drive_role==0 || drive_role==3));
 burn_drive_set_speed(drive, xorriso->speed, xorriso->speed);
 burn_write_opts_set_underrun_proof(*burn_options, 1);
 return(1);
}


/* @param flag bit0= do not write but only prepare and return size in sectors
               bit1= do not use isoburn wrappers
*/
int Xorriso_sanitize_image_size(struct XorrisO *xorriso,
                    struct burn_drive *drive, struct burn_disc *disc,
                    struct burn_write_opts *burn_options, int flag)
{
 int ret, img_sectors, num_sessions= 0, num_tracks= 0, padding= 0, profile;
 int media_space;
 char profile_name[80];
 struct burn_session **sessions;
 struct burn_track **tracks;

 img_sectors= burn_disc_get_sectors(disc);

 sessions= burn_disc_get_sessions(disc, &num_sessions);
 if(sessions==NULL || num_sessions < 1) {
no_track:;
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,"Program error : no track in prepared disc");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   {ret= -1; goto ex;}
 }
 tracks= burn_session_get_tracks(sessions[0], &num_tracks);
 if(tracks==NULL || num_tracks < 1)
   goto no_track;

 padding= 0;
 ret= burn_disc_get_profile(drive, &profile, profile_name);
 padding= xorriso->padding / 2048;
 if(xorriso->padding > padding * 2048)
   padding++;
 if(img_sectors>0 && (profile==0x09 || profile==0x0a)) { /* CD-R , CD-RW */
   if(img_sectors + padding < Xorriso_cd_min_track_sizE) {
     padding= Xorriso_cd_min_track_sizE - img_sectors;
     sprintf(xorriso->info_text,
             "Expanded track to minimum size of %d sectors",
             Xorriso_cd_min_track_sizE);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   }
 }
 burn_track_define_data(tracks[0], 0, padding * 2048, 0, BURN_MODE1);
 Xorriso_process_msg_queues(xorriso,0);

 if(flag&2)
   media_space= burn_disc_available_space(drive, burn_options) /
                (off_t) 2048;
 else
   media_space= isoburn_disc_available_space(drive, burn_options) /
                (off_t) 2048;
 if(media_space < img_sectors + padding) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,"Image size %ds exceeds free space on media %ds",
           img_sectors + padding, media_space);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   {ret= 0; goto ex;}
 }
 if(flag&1) {
   ret= img_sectors+padding;
 } else
   ret= 1;
ex:;
 return(ret);
}


/* @param flag bit0= do not write but only prepare and return size in sectors
*/
int Xorriso_write_session(struct XorrisO *xorriso, int flag)
{
 int ret, relax= 0, i;
 int major, minor, micro;
 char xorriso_id[256], *img_id;
 struct isoburn_imgen_opts *sopts= NULL;
 struct burn_drive_info *dinfo, *source_dinfo;
 struct burn_drive *drive, *source_drive;
 struct burn_disc *disc= NULL;
 struct burn_write_opts *burn_options;
 off_t readcounter= 0,writecounter= 0;
 int num_sessions= 0, num_tracks= 0;
 struct burn_session **sessions;
 struct burn_track **tracks;
 enum burn_disc_status s;
 IsoImage *image = NULL;
 ElToritoBootImage *bootimg;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to write", 2);
 if(ret<=0)
   return(0);

 if(xorriso->out_drive_handle == xorriso->in_drive_handle) {
   source_drive= drive;
 } else {
   if(xorriso->in_drive_handle == NULL) {
     source_drive= drive;
   } else {
     ret= Xorriso_get_drive_handles(xorriso, &source_dinfo, &source_drive,
                                    "on attempt to get source for write", 0);
     if(ret<=0)
       goto ex;
   }
   s= isoburn_disc_get_status(drive);
   if(s!=BURN_DISC_BLANK) {
     s= burn_disc_get_status(drive);
     if(s!=BURN_DISC_BLANK)
       sprintf(xorriso->info_text,
               "-indev differs from -outdev and -outdev media is not blank");
     else
       sprintf(xorriso->info_text,
        "-indev differs from -outdev and -outdev media holds valid ISO image");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     {ret= 0; goto ex;}
   }
 }

 ret= isoburn_igopt_new(&sopts, 0);
 if(ret<=0)
   return(ret);
 relax= isoburn_igopt_allow_deep_paths;

 /* Adjust or discard boot image */
 image= isoburn_get_attached_image(source_drive);
 /* >>> ??? move down to libisoburn ? */
 if(image!=NULL && !(flag&1)) {
   ret= iso_image_get_boot_image(image, &bootimg, NULL, NULL);
   if(xorriso->patch_isolinux_image) {
     if(ret==1) {
       relax|= isoburn_igopt_allow_full_ascii;
       sprintf(xorriso->info_text, "Patching alleged isolinux boot image");
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
       el_torito_patch_isolinux_image(bootimg);
     } else {
       sprintf(xorriso->info_text,
               "Could not find any boot image for -boot_image isolinux patch");
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0);
     }
   } else if(xorriso->keep_boot_image && ret==1) {
     relax|= isoburn_igopt_allow_full_ascii;
     sprintf(xorriso->info_text, "Keeping boot image unchanged");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   } else if(ret==1) {
     iso_image_remove_boot_image(image);
     sprintf(xorriso->info_text, "Discarded boot image from old session");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   }
 }
 isoburn_igopt_set_level(sopts, 2);
 isoburn_igopt_set_extensions(sopts, 1|((!!xorriso->do_joliet)<<1));
 isoburn_igopt_set_relaxed(sopts, isoburn_igopt_allow_deep_paths);
 isoburn_igopt_set_sort_files(sopts, 1);
 isoburn_igopt_set_over_mode(sopts, 0, 0, (mode_t) 0, (mode_t) 0);
 isoburn_igopt_set_over_ugid(sopts, 0, 0, (uid_t) 0, (gid_t) 0);
 isoburn_igopt_set_out_charset(sopts, NULL);
 isoburn_igopt_set_fifo_size(sopts, xorriso->fs * 2048);

 if(image!=NULL &&
    strlen(Xorriso_program_versioN)+strlen(Xorriso_timestamP)<80) {
   sprintf(xorriso_id, "XORRISO-%s %s",
           Xorriso_program_versioN, Xorriso_timestamP);
   isoburn_version(&major, &minor, &micro);
   if(strlen(xorriso_id)<80)
     sprintf(xorriso_id+strlen(xorriso_id),
             ", LIBISOBURN-%d.%d.%d", major, minor, micro);
   iso_lib_version(&major, &minor, &micro);
   if(strlen(xorriso_id)<80)
     sprintf(xorriso_id+strlen(xorriso_id),
             ", LIBISOFS-%d.%d.%d", major, minor, micro);
   burn_version(&major, &minor, &micro);
   if(strlen(xorriso_id)<80)
     sprintf(xorriso_id+strlen(xorriso_id),
             ", LIBBURN-%d.%d.%d", major, minor, micro);
   xorriso_id[128]= 0;
   img_id= (char *) iso_image_get_data_preparer_id(image);
   if(img_id!=NULL) {
     for(i= strlen(img_id)-1; i>=0 && img_id[i]==' '; i--);
     if(i>0) {
       sprintf(xorriso->info_text, "Overwrote previous preparer id '%s'",
               img_id);
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
     }
   }
   iso_image_set_data_preparer_id(image, xorriso_id);
 }

 /* Make final abort check before starting expensive activities */
 ret= Xorriso_eval_problem_status(xorriso, 1, 0);
 if(ret<0)
   {ret= 0; goto ex;}
 Xorriso_set_abort_severity(xorriso, 1);
 if(xorriso->out_drive_handle == xorriso->in_drive_handle ||
    xorriso->in_drive_handle == NULL) {
   ret= isoburn_prepare_disc(source_drive, &disc, sopts);
 } else {
   ret= isoburn_prepare_new_image(source_drive, &disc, sopts, drive);
 }
 if (ret <= 0) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,"Failed to prepare session write run");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   {ret= 0; goto ex;}
 }
 isoburn_igopt_get_effective_lba(sopts, &(xorriso->session_lba));

 ret= Xorriso_make_write_options(xorriso, drive, &burn_options, 0);
 if(ret<=0)
   goto ex;

 ret= Xorriso_sanitize_image_size(xorriso, drive, disc, burn_options, flag&1);
 if(ret<=0 || (flag&1)) {
   Xorriso_process_msg_queues(xorriso,0);
   if(flag&1) /* set queue severity to FAILURE */
     Xorriso_set_image_severities(xorriso, 2);
   isoburn_cancel_prepared_write(source_drive, drive, 0);
   if(flag&1) /* reset queue severity */
     Xorriso_set_image_severities(xorriso, 0);
   goto ex;
 }

 xorriso->run_state= 1; /* Indicate that burning has started */
 isoburn_disc_write(burn_options, disc);
 burn_write_opts_free(burn_options);

 ret= Xorriso_pacifier_loop(xorriso, drive, 0);
 if(ret<=0)
   goto ex;
 if(!isoburn_drive_wrote_well(drive)) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,
           "libburn indicates failure with writing.");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }

 sessions= burn_disc_get_sessions(disc, &num_sessions);
 if(num_sessions>0) {
   tracks= burn_session_get_tracks(sessions[0], &num_tracks);
   if(tracks!=NULL && num_tracks>0) {
     burn_track_get_counters(tracks[0],&readcounter,&writecounter);
     xorriso->session_blocks= (int) (writecounter/ (off_t) 2048);
     sprintf(xorriso->info_text,
    "ISO image produced: %d sectors. Written to media: %d sectors at LBA %d\n",
        (int) (readcounter/ (off_t) 2048),
        xorriso->session_blocks, xorriso->session_lba);
     Xorriso_info(xorriso, 0);
   }
 }

 ret= isoburn_activate_session(drive);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret<=0) {
   sprintf(xorriso->info_text,
           "Could not write new set of volume descriptors");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   goto ex;
 }
 /* Done early to free any reference to the libisofs resources via disc */
 if(disc!=NULL)
   burn_disc_free(disc);
 disc= NULL;
 /* To wait for the end of the libisofs threads and their messages. */
 isoburn_sync_after_write(source_drive, drive, 0);
 Xorriso_process_msg_queues(xorriso,0);

 sprintf(xorriso->info_text, "Writing completed sucessfully.\n\n");
 Xorriso_info(xorriso, 0);
 ret= 1;
ex:;
 xorriso->run_state= 0; /* Indicate that burning has ended */
 Xorriso_set_abort_severity(xorriso, 0);

 if(ret<=0) {
   /* >>> ??? revive discarded boot image */;
 }

 if(disc!=NULL)
   burn_disc_free(disc);
 isoburn_igopt_destroy(&sopts, 0);
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
} 


int Xorriso_check_burn_abort(struct XorrisO *xorriso, int flag)
{
 int ret;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;

 if(xorriso->run_state!=1)
   return(0);
 ret= Xorriso_eval_problem_status(xorriso, 1, 1);
 if(ret>=0)
   return(0);
 sprintf(xorriso->info_text,
         "-abort_on '%s' encountered '%s' during image writing",
         xorriso->abort_on_text, xorriso->problem_status_text);
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0,
                     xorriso->problem_status_text, 0);

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to abort burn run", 2);
 if(ret<=0)
   return(0);

 burn_drive_cancel(drive);
 sprintf(xorriso->info_text,
         "libburn has now been urged to cancel its operation");
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
 return(1);
}


/* This loop watches burn runs until they end.
   It issues pacifying update messages to the user.
   @param flag bit0-3 = emulation mode
                        0= xorriso
                        1= cdrskin
               bit4= report speed in CD units
*/
int Xorriso_pacifier_loop(struct XorrisO *xorriso, struct burn_drive *drive,
                          int flag)
{
 int ret, size, free_bytes, i, aborting= 0, emul, buffer_fill= 50, last_sector;
 struct burn_progress progress;
 char *status_text;
 enum burn_drive_status drive_status;
 double start_time, current_time, last_time;
 double measured_speed, speed_factor= 1385000;

 start_time= Sfile_microtime(0);
 while(burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING)
   usleep(100002);

 emul= flag&15;
 if(flag&16)
   speed_factor= 150.0*1024;
 progress.sector= 0;
 current_time= Sfile_microtime(0);
 while(1) {
   last_time= current_time;
   last_sector= progress.sector;
   drive_status= burn_drive_get_status(drive, &progress);
   if(drive_status == BURN_DRIVE_IDLE)
 break;
   current_time= Sfile_microtime(0);
   if(drive_status == BURN_DRIVE_WRITING && progress.sectors > 0) {
     if(emul==1) {
       if(progress.sector<=progress.sectors)
         sprintf(xorriso->info_text, "%4d of %4d MB written",
                 progress.sector / 512, progress.sectors / 512);
       else
         sprintf(xorriso->info_text, "%4d MB written",
                 progress.sector / 512);

       if(xorriso->pacifier_fifo!=NULL) {
         ret= burn_fifo_inquire_status(xorriso->pacifier_fifo,
                                       &size, &free_bytes, &status_text);
         if(ret>0 )
           sprintf(xorriso->info_text+strlen(xorriso->info_text),
                   " (fifo %2d%%)", 
                   (int) (100.0-100.0*((double) free_bytes)/(double) size));
       }

       buffer_fill= 50;
       if(progress.buffer_capacity>0)
         buffer_fill= (double) (progress.buffer_capacity
                                - progress.buffer_available) * 100.0
                      / (double) progress.buffer_capacity;
       sprintf(xorriso->info_text+strlen(xorriso->info_text), " [buf %3d%%]",
               buffer_fill);

       if(current_time-last_time>0.2) {
         measured_speed= (progress.sector - last_sector) * 2048.0 /
                         (current_time - last_time);
         sprintf(xorriso->info_text+strlen(xorriso->info_text), "  %4.1fx.",
                 measured_speed/speed_factor);
       }

     } else {
       if(progress.sector<=progress.sectors)
         sprintf(xorriso->info_text, "Writing: sector %d of %d",
                 progress.sector, progress.sectors);
       else
         sprintf(xorriso->info_text, "Writing: sector %d", progress.sector);
       ret= isoburn_get_fifo_status(drive, &size, &free_bytes, &status_text);
       if(ret>0 )
         sprintf(xorriso->info_text+strlen(xorriso->info_text),
                 "  [fifo %s, %2d%% fill]", status_text,
                 (int) (100.0-100.0*((double) free_bytes)/(double) size));
     }
   } else if(drive_status == BURN_DRIVE_CLOSING_SESSION ||
             drive_status == BURN_DRIVE_CLOSING_TRACK)
     sprintf(xorriso->info_text,
             "Closing track/session. Working since %.f seconds",
             current_time-start_time);
   else if(drive_status == BURN_DRIVE_FORMATTING)
     sprintf(xorriso->info_text, "Formatting. Working since %.f seconds",
             current_time-start_time);
   else
     sprintf(xorriso->info_text,
             "Thank you for being patient since %.f seconds",
             current_time-start_time);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0);

   for(i= 0; i<10; i++) {
     Xorriso_process_msg_queues(xorriso, 0);
     if(aborting<=0)
       aborting= Xorriso_check_burn_abort(xorriso, 0);
     usleep(100000);
   }
 }
 return(1);
}


int Xorriso__read_pacifier(IsoImage *image, IsoFileSource *filesource)
{
 struct XorrisO *xorriso;

 xorriso= (struct XorrisO *) iso_image_get_attached_data(image);
 if(xorriso==NULL)
   return(1);
 xorriso->pacifier_count++;
 if(xorriso->pacifier_count%10)
   return(1);
 Xorriso_pacifier_callback(xorriso, "nodes read", xorriso->pacifier_count, 0,
                           "", 0);
 return(1);
}


int Xorriso_get_volume(struct XorrisO *xorriso, IsoImage **volume,
                       int flag)
{
 if(xorriso->in_volset_handle==NULL) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,"No ISO image present.");
   if(xorriso->indev[0]==0 && xorriso->outdev[0]==0)
     sprintf(xorriso->info_text+strlen(xorriso->info_text),
             " No -dev, -indev, or -outdev selected.");
   else
     sprintf(xorriso->info_text+strlen(xorriso->info_text),
             " Possible program error with drive '%s'.", xorriso->indev);

   if(!xorriso->no_volset_present)
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   xorriso->no_volset_present= 1;
   return(0);
 }
 *volume= (IsoImage *) xorriso->in_volset_handle;
 xorriso->no_volset_present= 0;
 return(*volume != NULL);
}


/* @param flag bit0=do not complain about non existent node */
int Xorriso_node_from_path(struct XorrisO *xorriso, IsoImage *volume,
                           char *path, IsoNode **node, int flag)
{
 int ret;
 char sfe[5*SfileadrL], *path_pt;

 path_pt= path;
 if(path[0]==0)
   path_pt= "/";
 *node= NULL;
 ret= iso_tree_path_to_node(volume, path_pt, node);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret<=0) {
   if(!(flag&1)) {
     sprintf(xorriso->info_text, "Cannot find path %s in loaded ISO image",
             Text_shellsafe(path_pt, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   }
   return(0);
 }
 return(1);
}


/* @param eff_path  returns resulting effective path.
                    Must provide at least SfileadrL bytes of storage.
   @param flag bit0= do not produce problem events (unless faulty path format)
               bit1= work purely literally, do not use libisofs
               bit2= (with bit1) this is an address in the disk world
   @return -1 = faulty path format, 0 = not found ,
            1 = found simple node , 2 = found directory
*/
int Xorriso_normalize_img_path(struct XorrisO *xorriso, char *wd,
                               char *img_path, char eff_path[], int flag)
{
 int ret, is_dir= 0, done= 0;
 IsoImage *volume;
 IsoDir *dir= NULL;
 IsoNode *node= NULL;
 char path[SfileadrL], *apt, *npt, sfe[5*SfileadrL], *cpt;

 eff_path[0]= 0;
 if(img_path[0]==0)
   return(2); /* root directory */

 if(!(flag&2)) {
   ret= Xorriso_get_volume(xorriso, &volume, 0);
   if(ret<=0)
     return(ret);
 }

 apt= npt= path;
 if(img_path[0]!='/') {
   strcpy(path, wd);
   ret= Sfile_add_to_path(path, img_path, 0);
   if(ret<=0)
     goto much_too_long;
 } else
   if(Sfile_str(path, img_path, 0)<=0)
     return(-1);

 if(path[0]!='/') {
   sprintf(xorriso->info_text,
        "Internal error: Unresolvable relative addressing in iso_rr_path '%s'",
        img_path);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FATAL", 0);
   return(-1);
 } else if(path[1]==0)
   return(2); /* root directory */

 for(npt= apt; !done; apt= npt+1) {
   npt= strchr(apt, '/');
   if(npt==NULL) {
     npt= apt+strlen(apt);
     done= 1;
   } else
     *npt= 0;
   if(*apt==0) {
     *apt= '/';
     apt++;
     if(done)
 break;
 continue;
   }
   if(strcmp(apt,".")==0) {
     is_dir= 1;
 continue;
   }
   if(strcmp(apt,"..")==0) {
     if(!(flag&2)) {
       node= (IsoNode *) dir;
       if(node==NULL) {
bonked_root:;
         sprintf(xorriso->info_text,
                 "Relative addressing in path exceeds root directory: %s",
                 Text_shellsafe(img_path, sfe, 0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         return(-1);
       }
       dir= iso_node_get_parent(node);
     }
     /* truncate eff_path */;
     cpt= strrchr(eff_path, '/');
     if(cpt==NULL) /* ??? if not flag&2 then this is a bug */
       goto bonked_root;
     *cpt= 0;
     is_dir= 1;
 continue;
   }
   ret= Sfile_add_to_path(eff_path, apt, 0);
   if(ret<=0) {
much_too_long:;
     sprintf(xorriso->info_text, "Effective path gets much too long (%d)",
             (int) (strlen(eff_path)+strlen(apt)+1));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     return(-1);
   }
   if(!(flag&2)) {
     dir= (IsoDir *) node;
     ret= Xorriso_node_from_path(xorriso, volume, eff_path, &node, flag&1);
     if(ret<=0) 
       return(0);
     if(dir==NULL) /* could be false with  "/dir/.." */
       dir= iso_node_get_parent(node);
     is_dir= LIBISO_ISDIR(node);
   }
 }
 return(1+!!is_dir);
}


int Xorriso_get_node_by_path(struct XorrisO *xorriso,
                             char *in_path, char *eff_path,
                             IsoNode **node, int flag)
{
 int ret;
 char path[SfileadrL];
 IsoImage *volume;

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, in_path, path, 0);
 if(ret<=0)
   return(ret);
 if(eff_path!=NULL)
   strcpy(eff_path, path);
 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);
 ret= Xorriso_node_from_path(xorriso, volume, path, node, 0);
 if(ret<=0)
   return(0);
 return(1);
}


/* @param flag bit0= give directory x-permission where is r-permission
*/
int Xorriso_transfer_properties(struct XorrisO *xorriso, struct stat *stbuf,
                                IsoNode *node, int flag)
{
 mode_t mode;

 mode= stbuf->st_mode;
 if((flag&1) && S_ISDIR(mode)) {
   if(mode&S_IRUSR)
     mode|= S_IXUSR;
   if(mode&S_IRGRP)
     mode|= S_IXGRP;
   if(mode&S_IROTH)
     mode|= S_IXOTH;
 }
 iso_node_set_permissions(node, mode & 07777);
 iso_node_set_uid(node, stbuf->st_uid);
 iso_node_set_gid(node, stbuf->st_gid);
 iso_node_set_atime(node, stbuf->st_atime);
 iso_node_set_mtime(node, stbuf->st_mtime);
 iso_node_set_ctime(node, stbuf->st_ctime);
 return(1);
}


int Xorriso_graft_split(struct XorrisO *xorriso, IsoImage *volume,
                        IsoDir *dir, char *disk_path, char *img_name,
                        char *nominal_source, char *nominal_target,
                        off_t size, IsoNode **node, int flag)
{
 int ret;
 IsoDir *new_dir= NULL;
 IsoNode *part_node;
 int partno, total_parts;
 off_t offset;
 char part_name[SfileadrL], sfe[5*SfileadrL];

 ret= iso_tree_add_new_dir(dir, img_name, &new_dir);
 if(ret<0)
   return(ret);
 *node= (IsoNode *) new_dir;

 total_parts= size / xorriso->split_size;
 if(size % xorriso->split_size)
   total_parts++;
 for(partno= 1; partno<=total_parts; partno++) {
   offset = xorriso->split_size * (off_t) (partno-1);
   Splitpart__compose(part_name, partno, total_parts, offset,
                      xorriso->split_size, size, 0);
   ret= Xorriso_tree_graft_node(xorriso, volume,
                                new_dir, disk_path, part_name,
                                nominal_source, nominal_target,
                                offset, xorriso->split_size,
                                &part_node, 8);
   if(ret<=0)
     return(0);
 }
 sprintf(xorriso->info_text, "Split into %d parts: %s",
         total_parts, Text_shellsafe(nominal_target, sfe, 0));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
 return(1);
}


/* 
   @param flag bit3= cut_out_node: offset and size are valid
*/
int Xorriso_tree_graft_node(struct XorrisO *xorriso, IsoImage *volume,
                            IsoDir *dir, char *disk_path, char *img_name,
                            char *nominal_source, char *nominal_target,
                            off_t offset, off_t cut_size,
                            IsoNode **node, int flag)
{
 int ret;
 struct stat stbuf;
 char sfe[5*SfileadrL];
 off_t size= 0;
 static off_t limit= ((off_t) 4) * ((off_t) 1024*1024*1024) - (off_t) 1;

 if(flag&8)  {
   if(cut_size > limit) {
     sprintf(xorriso->info_text,
             "File piece exceeds size limit of %.f bytes: %.f from %s\n",
             (double) limit, (double) cut_size,
             Text_shellsafe(disk_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     return(0);
   }
   ret= iso_tree_add_new_cut_out_node(volume, dir, img_name, disk_path,
                                      offset, cut_size, node);
   if(ret<0)
     goto ex;
 } else {

   if(stat(disk_path, &stbuf)!=-1)
     if(S_ISREG(stbuf.st_mode))
       size= stbuf.st_size;
   if(xorriso->split_size > 0 && size > xorriso->split_size) {
     ret= Xorriso_graft_split(xorriso, volume, dir, disk_path, img_name,
                              nominal_source, nominal_target, size,
                              node, 0);
     if(ret<=0)
       goto ex;
     return(1);
   } else if(size > limit) {
     sprintf(xorriso->info_text,
             "File exceeds size limit of %.f bytes: %s\n",
             (double) limit, Text_shellsafe(disk_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     return(0);
   }
   ret= iso_tree_add_new_node(volume, dir, img_name, disk_path, node);
   if(ret<0)
     goto ex;
 }
ex:;
 if(ret<0) {
   Xorriso_process_msg_queues(xorriso,0);
   Xorriso_report_iso_error(xorriso, nominal_source, ret,
                            "Cannot add node to tree", 0, "FAILURE", 1|2);
   return(ret);
 }
 return(1);
}


/* @param flag bit0= recursion is active
               bit1= do not report added files
*/
int Xorriso_add_tree(struct XorrisO *xorriso, IsoDir *dir,
                     char *img_dir_path, char *disk_dir_path,
                     struct LinkiteM *link_stack, int flag)
{
 IsoImage *volume;
 IsoNode *node;
 IsoSymlink *iso_symlink;
 int ret, target_is_dir, source_is_dir, source_is_link, fret, was_failure= 0;
 int do_not_dive, target_is_split= 0;
 struct DirseQ *dirseq= NULL;
 char *name, *img_name, *srcpt, *stbuf_src= "";
 struct stat stbuf, hstbuf;
 dev_t dir_dev;
 struct LinkiteM *own_link_stack;

#ifdef Xorriso_fat_local_meM
 char sfe[5*SfileadrL], sfe2[5*SfileadrL];
 char disk_path[2*SfileadrL], img_path[2*SfileadrL], link_target[SfileadrL];
#else /* Xorriso_fat_local_meM */

 char *sfe= NULL, *sfe2= NULL;
 char *disk_path= NULL, *img_path= NULL, *link_target= NULL;

 /* Avoiding large local memory objects in order to save stack space */
 sfe= malloc(5*SfileadrL);
 sfe2= malloc(5*SfileadrL);
 disk_path= malloc(2*SfileadrL);
 img_path= malloc(2*SfileadrL);
 link_target= malloc(SfileadrL);
 if(sfe==NULL || sfe2==NULL || disk_path==NULL || img_path==NULL ||
    link_target==NULL) {
   Xorriso_no_malloc_memory(xorriso, &sfe, 0);
   {ret= -1; goto ex;}
 }
   
#endif /* ! Xorriso_fat_local_meM */

 own_link_stack= link_stack;

 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   goto ex;

 stbuf_src= disk_dir_path;
 if(lstat(disk_dir_path, &stbuf)==-1)
   goto cannot_open_dir;
 dir_dev= stbuf.st_dev;
 if(S_ISLNK(stbuf.st_mode)) {
   if(!(xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&1))))
     {ret= 2; goto ex;}
   stbuf_src= disk_dir_path;
   if(stat(disk_dir_path, &stbuf)==-1)
     goto cannot_open_dir;
   if(dir_dev != stbuf.st_dev &&
      !(xorriso->do_follow_mount || (xorriso->do_follow_param && !(flag&1))))
     {ret= 2; goto ex;}
 }
 ret= Dirseq_new(&dirseq, disk_dir_path, 1);
 if(ret<0) {
   sprintf(xorriso->info_text,"Failed to create source filesystem iterator");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   {ret= -1; goto ex;}
 }
 if(ret==0) {
cannot_open_dir:;
   Xorriso_msgs_submit(xorriso, 0, disk_dir_path, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text,"Cannot open as source directory: %s",
           Text_shellsafe(disk_dir_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   {ret= 0; goto ex;}
 }

 if(Sfile_str(disk_path, disk_dir_path,0)<=0)
   {ret= -1; goto ex;}
 if(disk_path[0]==0 || disk_path[strlen(disk_path)-1]!='/')
   strcat(disk_path,"/");
 name= disk_path+strlen(disk_path);
 if(Sfile_str(img_path, img_dir_path, 0)<=0)
   {ret= -1; goto ex;}
 if(img_path[0] || img_path[strlen(img_path)-1]!='/')
   strcat(img_path,"/");
 img_name= img_path+strlen(img_path);

 while(1) { /* loop over directory content */
   stbuf_src= "";
   Linkitem_reset_stack(&own_link_stack, link_stack, 0);
   srcpt= disk_path;
   Xorriso_process_msg_queues(xorriso,0);
   ret= Dirseq_next_adr(dirseq,name,0); /* name is a pointer into disk_path */
   if(ret==0)
 break;
   if(ret<0) {
     sprintf(xorriso->info_text,"Failed to obtain next directory entry");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
     {ret= -1; goto ex;}
   }

   /* >>> compare exclusions against disk_path resp. name */;

   strcpy(img_name, name);
   if(Xorriso_much_too_long(xorriso, strlen(img_path), 0)<=0)
     {ret= 0; goto was_problem;}
   if(Xorriso_much_too_long(xorriso, strlen(srcpt), 0)<=0)
     {ret= 0; goto was_problem;}
   stbuf_src= srcpt;
   if(lstat(srcpt, &stbuf)==-1) {
cannot_lstat:;
     Xorriso_msgs_submit(xorriso, 0, srcpt, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text,
             "Cannot determine attributes of source file %s",
             Text_shellsafe(srcpt, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
     ret= 0; goto was_problem;
   }
   source_is_dir= 0;
   source_is_link= S_ISLNK(stbuf.st_mode);
   if(xorriso->do_follow_links && source_is_link) {
     /* Xorriso_hop_link checks for wide link loops */
     ret= Xorriso_hop_link(xorriso, srcpt, &own_link_stack, &hstbuf, 0);
     if(ret<0)
       goto was_problem;
     if(ret==1) {
       ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 0);
       if(ret<=0)
         goto was_problem;
       srcpt= link_target;
       stbuf_src= srcpt;
       if(lstat(srcpt, &stbuf)==-1)
         goto cannot_lstat;
     } else {
       if(Xorriso_eval_problem_status(xorriso, 0, 1|2)<0)
         {ret= 0; goto was_problem;} 
     }
   } else if (S_ISLNK(stbuf.st_mode)) {
     ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 1);
     if(ret<=0)
       goto was_problem;
   }
   do_not_dive= 0;
   if(S_ISDIR(stbuf.st_mode)) {
     source_is_dir= 1;
     if(dir_dev != stbuf.st_dev && !xorriso->do_follow_mount)
       do_not_dive= 1;
   }

   /* does a node exist with this name ? */
   node= NULL;
   ret= Xorriso_node_from_path(xorriso, volume, img_path, &node, 1);
   if(ret>0) {
     target_is_dir= LIBISO_ISDIR(node);
     target_is_split= 0;
     if(target_is_dir)
       target_is_split= Xorriso_is_split(xorriso, node, 0);

     if(!((target_is_dir && !target_is_split) && source_is_dir)) {
       Xorriso_process_msg_queues(xorriso,0);

       /* handle overwrite situation */;
       if(xorriso->do_overwrite==1 ||
          (xorriso->do_overwrite==2 && !(target_is_dir && !target_is_split))) {
         ret= Xorriso_rmi(xorriso, NULL, (off_t) 0, img_path, 1|8);
         if(ret<=0)
           goto was_problem;
         if(ret==3) {
           sprintf(xorriso->info_text, "User revoked adding of: %s",
                   Text_shellsafe(img_path, sfe, 0));
           Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
           ret= 0; goto was_problem;
         }
         node= NULL;
       } else {
         Xorriso_msgs_submit(xorriso, 0, srcpt, 0, "ERRFILE", 0);
         sprintf(xorriso->info_text,
     "While grafting %s : file object exists and may not be overwritten by %s",
             Text_shellsafe(img_path,sfe,0), Text_shellsafe(stbuf_src,sfe2,0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         ret= 0; goto was_problem;
       }
     }
   }

   if(node==NULL) {
     if(S_ISLNK(stbuf.st_mode)) {

       /* ??? NG : A80107 : is this solved now ? */
       /* <<< One should rather change libisofs so that iso_tree_add_node()
              adds a disk_link as RR link, if RR is enabled */

       ret= iso_tree_add_new_symlink(dir, img_name, link_target, &iso_symlink);
       node= (IsoNode *) iso_symlink;
       if(ret>0) {
         ret= Xorriso_transfer_properties(xorriso, &stbuf, node, 0);
         if(ret<=0)
           goto was_problem;
       } else {
         Xorriso_report_iso_error(xorriso, stbuf_src, ret,
                             "Cannot create symbolic link", 0, "FAILURE", 1|2);
         {ret= 0; goto was_problem;}
       }
     } else {
       ret= Xorriso_tree_graft_node(xorriso, volume, dir, srcpt, img_name,
                                    "", img_path, (off_t) 0, (off_t) 0,
                                    &node, 0);
     }
   }
   if(node==NULL) {
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_msgs_submit(xorriso, 0, stbuf_src, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text, "Grafting failed:  %s = %s",
             Text_shellsafe(img_path,sfe,0), Text_shellsafe(stbuf_src,sfe2,0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret=0; goto was_problem;
   }

   xorriso->pacifier_count++;
   if((xorriso->pacifier_count%100)==0)
     Xorriso_pacifier_callback(xorriso, "files added", xorriso->pacifier_count,
                               xorriso->pacifier_total, "", 0);

   xorriso->volset_change_pending= 1;
   if(source_is_dir) {
     if(do_not_dive) {
       sprintf(xorriso->info_text, "Did not follow mount point : %s",
               Text_shellsafe(disk_path, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     } else {
       ret= Xorriso_add_tree(xorriso, (IsoDir *) node,
                             img_path, disk_path, own_link_stack, 1|(flag&2));
     }
     if(ret<=0)
       goto was_problem;
   }

 continue; /* regular bottom of loop */
was_problem:;
   was_failure= 1;
   fret= Xorriso_eval_problem_status(xorriso, ret, 1|2);
   if(fret<0)
     goto ex;
 }

 ret= 1;
ex:

#ifndef Xorriso_fat_local_meM
 if(sfe!=NULL)
   free(sfe);
 if(sfe2!=NULL)
   free(sfe2);
 if(disk_path!=NULL)
   free(disk_path);
 if(img_path!=NULL)
   free(img_path);
 if(link_target!=NULL)
   free(link_target);
#endif /* ! Xorriso_fat_local_meM */

 Xorriso_process_msg_queues(xorriso,0);
 Linkitem_reset_stack(&own_link_stack, link_stack, 0);
 Dirseq_destroy(&dirseq, 0);
 if(ret<=0)
   return(ret);
 return(!was_failure);
}


/* @param flag bit0= cut_out mode : base on leaf parent directory
*/
int Xorriso_copy_implict_properties(struct XorrisO *xorriso, IsoDir *dir,
           char *full_img_path, char *img_path, char *full_disk_path, int flag)
{
 int ret, nfic, nic, nfdc, d, i;
 char nfi[SfileadrL], ni[SfileadrL], nfd[SfileadrL], *cpt;
 char sfe[5*SfileadrL];
 struct stat stbuf;

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, full_img_path, nfi,
                                 1|2);
 if(ret<=0)
   return(ret);
 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, img_path, ni, 1|2);
 if(ret<=0)
   return(ret);
 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, full_disk_path, nfd,
                                 1|2|4);
 if(ret<=0)
   return(ret);
 nfic= Sfile_count_components(nfi, 0);
 nic= Sfile_count_components(ni, 0);
 nfdc= Sfile_count_components(nfd, 0);
 d= nfic-(flag&1)-nic;
 if(d<0)
   return(-1);
 if(d>nfdc)
   return(0);
 for(i= 0; i<d; i++) {
   cpt= strrchr(nfd, '/');
   if(cpt==NULL)
     return(-1); /* should not happen */
   *cpt= 0;
 }
 if(nfd[0]==0)
   strcpy(nfd, "/");
 if(stat(nfd, &stbuf)==-1)
   return(0);
 Xorriso_transfer_properties(xorriso, &stbuf, (IsoNode *) dir,
                             ((flag&1) && d==0));
 sprintf(xorriso->info_text,
         "Copied properties for %s", Text_shellsafe(ni, sfe, 0));
 sprintf(xorriso->info_text+strlen(xorriso->info_text),
         " from %s", Text_shellsafe(nfd, sfe, 0));
 if(!((flag&1) && d==0))
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 return(1);
}


/* @param bit0= copy link target properties rather than link properties
          bit1= give directory x-permission where is r-permission
*/
int Xorriso_copy_properties(struct XorrisO *xorriso,
                            char *disk_path, char *img_path, int flag)
{
 int ret;
 IsoNode *node;
 struct stat stbuf;

 ret= Xorriso_get_node_by_path(xorriso, img_path, NULL, &node, 0);
 if(ret<=0)
   return(ret);
 if(lstat(disk_path, &stbuf)==-1)
   return(0);
 Xorriso_transfer_properties(xorriso, &stbuf, node, (flag&2)>>1);
 xorriso->volset_change_pending= 1;
 return(1);
}


/* @param boss_iter Opaque handle to be forwarded to actions in ISO image
                    Set to NULL if calling this function from outside ISO world
   @param flag bit0= mkdir: graft in as empty directory, not as copy from disk
               bit1= do not report added files
               bit2= -follow: this is not a command parameter
               bit3= use offset and cut_size for cut_out_node
   @return <=0 = error , 1 = added simple node , 2 = added directory
*/
int Xorriso_graft_in(struct XorrisO *xorriso, void *boss_iter, 
                     char *disk_path, char *img_path,
                     off_t offset, off_t cut_size, int flag)
{
 IsoImage *volume;
 char path[SfileadrL], *apt, *npt, *cpt, sfe[5*SfileadrL], sfe2[5*SfileadrL];
 char *disk_path_pt, resolved_disk_path[SfileadrL];
 IsoDir *dir, *hdir;
 IsoNode *node;
 int done= 0, is_dir= 0, l, ret, target_is_dir, source_is_dir, resolve_link= 0;
 int target_is_split;
 struct stat stbuf;


 /* >>> compare exclusions against disk_path resp. name 
        but no leaf patter if flag&4 */;


 for(cpt= img_path; 1; cpt++) {
   cpt= strstr(cpt,"/.");
   if(cpt==NULL)
 break;
   if(cpt[2]=='.') {
     if(cpt[3]=='/' || cpt[3]==0)
 break;
   } else if(cpt[2]=='/' || cpt[2]==0)
 break;
 }
 if(cpt!=NULL) {
   Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text,
         "Unsupported relative addressing in iso_rr_path %s (disk: %s)",
         Text_shellsafe(img_path, sfe, 0), Text_shellsafe(disk_path, sfe2, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   return(0);
 }

 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);

 strncpy(path, img_path, sizeof(path)-1);
 path[sizeof(path)-1]= 0;
 apt= npt= path;

 if(!(flag&1)) {
   ret= lstat(disk_path, &stbuf);
   if(ret!=-1) {
     if(S_ISDIR(stbuf.st_mode))
       is_dir= 1;
     else if((stbuf.st_mode&S_IFMT)==S_IFLNK &&
             (xorriso->do_follow_links ||
              (xorriso->do_follow_param && !(flag&4)))) {
       resolve_link= 1;
       ret= stat(disk_path, &stbuf);
       if(ret!=-1) {
         if(S_ISDIR(stbuf.st_mode))
           is_dir= 1;
       }
     }
   }
   if(ret == -1) {
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text,
            "Cannot determine attributes of source file %s",
            Text_shellsafe(disk_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
     return(0);
   }
   if(S_ISDIR(stbuf.st_mode)) {
     is_dir= 1;
   } else {
     l= strlen(img_path);
     if(l>0)
       if(img_path[l-1]=='/')
         l= 0;
     if(l==0) {
       Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
       sprintf(xorriso->info_text,
         "Source '%s' is not a directory. Target '%s' would be.",
         Text_shellsafe(disk_path, sfe, 0), Text_shellsafe(img_path, sfe2, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       return(0);
     }
   }
 }

 dir= iso_image_get_root(volume);
 if(dir==NULL) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,
           "While grafting '%s' : no root node available", img_path);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(0);
 }
 for(npt= apt; !done; apt= npt+1) {
   npt= strchr(apt, '/');
   if(npt==NULL) {
     npt= apt+strlen(apt);
     done= 1;
   } else
     *npt= 0;
   if(*apt==0) {
     *apt= '/';
     apt++;
     if(done)
       goto attach_source;
 continue;
   }
   source_is_dir= (is_dir || (flag&1) || !done);
   ret= Xorriso_node_from_path(xorriso, volume, path, &node, 1);
   if(ret>0) {
     target_is_dir= LIBISO_ISDIR(node);

     target_is_split= 0;
     if(target_is_dir) 
       target_is_split= Xorriso_is_split(xorriso, node, 0);

     if(!((target_is_dir && !target_is_split) && source_is_dir)) {
       Xorriso_process_msg_queues(xorriso,0);

       /* handle overwrite situation */;
       if(xorriso->do_overwrite==1 ||
          (xorriso->do_overwrite==2 && !(target_is_dir && !target_is_split))) {
         ret= Xorriso_rmi(xorriso, boss_iter, (off_t) 0, path, 1|8);
         if(ret<=0)
           return(ret);
         if(ret==3) {
           sprintf(xorriso->info_text, "User revoked adding of: %s",
                   Text_shellsafe(disk_path, sfe, 0));
           Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
           return(0);
         }
         node= NULL;
         goto handle_path_node;
       }

       Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
       sprintf(xorriso->info_text,
              "While grafting '%s' : '%s' exists and may not be overwritten",
              img_path, path);
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       return(0);
     }
     dir= (IsoDir *) node;
   }

handle_path_node:;
   if(node==NULL && source_is_dir) { /* make a directory */
     ret= iso_tree_add_new_dir(dir, apt, &hdir);
     if(ret<0) {
       Xorriso_process_msg_queues(xorriso,0);
       Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
       Xorriso_report_iso_error(xorriso, img_path, ret,
                                "Cannot create directory", 0, "FAILURE", 1);
       sprintf(xorriso->info_text,
               "While grafting '%s' : could not insert '%s'", img_path, path);
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       return(0);
     }
     dir= hdir;
     xorriso->volset_change_pending= 1;
     iso_node_set_ctime((IsoNode *) dir, time(NULL));
     iso_node_set_uid((IsoNode *) dir, geteuid());
     iso_node_set_gid((IsoNode *) dir, getegid());

     if(disk_path!=NULL && !done)
       Xorriso_copy_implict_properties(xorriso, dir, img_path, path, disk_path,
                                       !!(flag&8));

   }
   if(done) {
attach_source:;
     if(flag&1) {
       /* directory node was created above */;

     } else if(is_dir) {
       Xorriso_transfer_properties(xorriso, &stbuf, (IsoNode *) dir, 0);
       ret= Xorriso_add_tree(xorriso, dir, img_path, disk_path, NULL, flag&2);
       if(ret<=0)
         return(ret);

     } else {
       if(resolve_link) {
         ret= Xorriso_resolve_link(xorriso, disk_path, resolved_disk_path, 0);
         if(ret<=0)
           return(ret);
         disk_path_pt= resolved_disk_path;
       } else
         disk_path_pt= disk_path;

       ret= Xorriso_tree_graft_node(xorriso, volume, dir, disk_path_pt, apt,
                                    disk_path, img_path,
                                    offset, cut_size, &node, flag&8);
       if(ret<0) {
         sprintf(xorriso->info_text, "Grafting failed:  %s = %s",
             Text_shellsafe(img_path,sfe,0), Text_shellsafe(disk_path,sfe2,0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         return(0);
       }
       xorriso->volset_change_pending= 1;
       iso_node_set_name(node, apt);

       xorriso->pacifier_count++;
       if(xorriso->pacifier_count%100 && !(flag&2))
         Xorriso_pacifier_callback(xorriso, "files added",
                                   xorriso->pacifier_count,
                                   xorriso->pacifier_total, "", 0);
     }
   } else
     *npt= '/';
 }
 Xorriso_process_msg_queues(xorriso,0);
 return(1+!!is_dir);
}


/* @param flag bit0= -follow: disk_path is not a command parameter
*/
int Xorriso_cut_out(struct XorrisO *xorriso, char *disk_path,
                off_t startbyte, off_t bytecount, char *iso_rr_path, int flag)
{
 int ret;
 char eff_source[SfileadrL], eff_dest[SfileadrL], sfe[SfileadrL*5];
 struct stat stbuf;

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, eff_source,
                                 2|4);
 if(ret<=0)
   return(ret);

 if(lstat(eff_source, &stbuf)==-1) {
   Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text, "-cut_out: Cannot determine type of %s",
           Text_shellsafe(eff_source, sfe, 0)); 
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   return(0);
 }

 if((stbuf.st_mode&S_IFMT) == S_IFLNK) {
   if(!(xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&1))))
     goto unsupported_type;
   if(stat(eff_source, &stbuf)==-1) {
     Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text,
             "-cut_out: Cannot determine link target type of %s",
             Text_shellsafe(eff_source, sfe, 0)); 
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0);
     return(0);
   }
 }
 if(S_ISREG(stbuf.st_mode)) {
   if(stbuf.st_size<startbyte) {
     Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text,
             "-cut_out: Byte offset %.f larger than file size %.f",
             (double) startbyte, (double) stbuf.st_size); 
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "SORRY", 0);
     return(0);
   }
 } else {
unsupported_type:;
   Xorriso_msgs_submit(xorriso, 0, eff_source, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text, "-cut_out: Unsupported file type (%s) with %s",
           Ftypetxt(stbuf.st_mode, 0), Text_shellsafe(eff_source, sfe, 0)); 
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   return(0);
 }

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, iso_rr_path, eff_dest,
                                 2|((flag&1)<<2));
 if(ret<=0)
   return(ret);

 ret= Xorriso_graft_in(xorriso, NULL, eff_source, eff_dest,
                       startbyte, bytecount, 8);
 return(ret);
}


int Xorriso_process_msg_queues(struct XorrisO *xorriso, int flag)
{
 int ret, error_code= 0, os_errno= 0, count= 0, pass, imgid, tunneled;
 char severity[80];

 if(!xorriso->libs_are_started)
   return(1);
 for(pass= 0; pass< 2; pass++) {
   while(1) {
     tunneled= 0;
     if(pass==0)
       ret= iso_obtain_msgs("ALL", &error_code, &imgid,
                            xorriso->info_text, severity);
     else {
       ret= burn_msgs_obtain("ALL", &error_code, xorriso->info_text, &os_errno,
                             severity);
       if((error_code>=0x00030000 && error_code<0x00040000) ||
          (error_code>=0x00050000 && error_code<0x00060000))
         tunneled= -1; /* "libisofs:" */
       else if(error_code>=0x00060000 && error_code<0x00070000)
         tunneled= 1;  /* "libisoburn:" */
     }
     if(ret<=0)
   break;

     /* <<< tunneled MISHAP from libisoburn through libburn
            or well known error codes of MISHAP events
            With libburn-0.4.4 this is not necessary */
     if(error_code==0x5ff73 || error_code==0x3ff73 ||
        error_code==0x3feb9 || error_code==0x3feb2)
       strcpy(severity, "MISHAP");
     else if(error_code==0x51001)
       strcpy(severity, "ERRFILE");

     Xorriso_msgs_submit(xorriso, error_code, xorriso->info_text, os_errno, 
                         severity, ((pass+tunneled)+1)<<2);
     count++;
   }
 }
 if(xorriso->library_msg_direct_print && count>0) {
   sprintf(xorriso->info_text,"   (%d library messages repeated by xorriso)\n",
           count);
   Xorriso_info(xorriso, 0);
 }
 return(1);
}


/* @param flag bit0=short report form
               bit1=report about output drive
*/
int Xorriso_toc(struct XorrisO *xorriso, int flag)
{
 int num_sessions= 0, num_tracks= 0, lba= 0, nwa= -1, pmin, psec, pframe, ret;
 int track_count= 0, session_no, track_no, profile_no= -1;
 int last_track_start= 0, last_track_size= -1, num_data= 0, is_data= 0;
 int is_inout_drive= 0, drive_role;
 int num_payload= 0, num_wasted= 0, num_nondata= 0;
 char profile_name[80],*respt,*devadr;
 struct burn_disc *disc= NULL;
 struct burn_session **sessions;
 struct burn_track **tracks;
 struct burn_toc_entry toc_entry;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 enum burn_disc_status s;
 char mem_text[80];
 off_t start_byte= 0, num_free= 0;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                               "on attempt to print Table Of Content", flag&2);
 if(ret<=0)
   return(0);

 respt= xorriso->result_line;

 if(strcmp(xorriso->indev, xorriso->outdev)==0)
   is_inout_drive= 1;
 if(flag&2)
   devadr= xorriso->outdev;
 else
   devadr= xorriso->indev;
 sprintf(respt, "Drive current: %s '%s'\n",
         (is_inout_drive ? "-dev" : (flag&2 ? "-outdev" : "-indev")),
         devadr);
 Xorriso_result(xorriso,0);
 sprintf(respt, "Drive type   : vendor '%s' product '%s' revision '%s'\n",
        dinfo[0].vendor, dinfo[0].product, dinfo[0].revision);
 if(!(flag&1))
   Xorriso_result(xorriso,0);
 
 sprintf(respt, "Media current: ");
 ret= burn_disc_get_profile(drive, &profile_no, profile_name);
 if (profile_no > 0 && ret > 0) {
   if (profile_name[0])
     sprintf(respt+strlen(respt), "%s", profile_name);
   else
     sprintf(respt+strlen(respt), "%4.4Xh", profile_no);
   drive_role= burn_drive_get_drive_role(drive);
   if(drive_role==2)
     sprintf(respt+strlen(respt), ", overwriteable");
   else if(drive_role==0 || drive_role==3)
     sprintf(respt+strlen(respt), ", sequential");
   strcat(respt, "\n");
 } else
    sprintf(respt+strlen(respt), "is not recognizable\n");
 Xorriso_result(xorriso,0);

 sprintf(respt, "Media status : ");
 s= isoburn_disc_get_status(drive);
 if (s == BURN_DISC_FULL) {
   sprintf(respt+strlen(respt), "is written , is closed\n");
 } else if (s == BURN_DISC_APPENDABLE) {
   sprintf(respt+strlen(respt), "is written , is appendable\n");
 } else if (s == BURN_DISC_BLANK) {
   sprintf(respt+strlen(respt), "is blank\n");
 } else if (s == BURN_DISC_EMPTY)
   sprintf(respt+strlen(respt), "is not present\n");
 else
   sprintf(respt+strlen(respt), "is not recognizable\n");
 Xorriso_result(xorriso,0);

 if(s != BURN_DISC_FULL && s != BURN_DISC_APPENDABLE)
   return(1);
  
 if(xorriso->request_to_abort)
   return(1);

 disc= burn_drive_get_disc(drive);
 if (disc==NULL) {
   Xorriso_process_msg_queues(xorriso,0);
     ret= isoburn_get_min_start_byte(drive, &start_byte, 0);
     nwa= start_byte / 2048;
   if(ret<=0) {
     Xorriso_process_msg_queues(xorriso,0);
     if(flag&1)
       return(0);
     sprintf(xorriso->info_text, "Cannot obtain Table Of Content");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0);
     return(0);
   }
   /* fabricate TOC */
   sprintf(respt, "Media content: session %2d  ", 1);
   sprintf(respt+strlen(respt), "track    %2d %s  lba: %9d\n", 1, "data ", 0);
   if(!(flag&1))
     Xorriso_result(xorriso,0);
   last_track_start= lba;
   sprintf(respt, "Media content: session %2d  ", 1);
   sprintf(respt+strlen(respt), "leadout            lba: %9d\n", nwa);
   if(!(flag&1))
     Xorriso_result(xorriso,0);
   num_payload= num_data= last_track_size= nwa;
   num_sessions= 1;
 } else {
   sessions= burn_disc_get_sessions(disc, &num_sessions);
   for (session_no= 0; session_no<num_sessions && !(xorriso->request_to_abort);
        session_no++) {
     tracks = burn_session_get_tracks(sessions[session_no], &num_tracks);
     if (tracks==NULL)
   continue;
     for(track_no= 0; track_no<num_tracks && !(xorriso->request_to_abort);
         track_no++) {
       track_count++;
       is_data= 0;
       burn_track_get_entry(tracks[track_no], &toc_entry);
       if (toc_entry.extensions_valid & 1) {
         /* DVD extension valid */
         lba= toc_entry.start_lba;
       } else {
         lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec, toc_entry.pframe);
       }
       sprintf(respt, "Media content: session %2d  ", session_no+1);
       sprintf(respt+strlen(respt), "track    %2d %s  lba: %9d\n",
               track_count, ((toc_entry.control&7)<4?"audio":"data "), lba);
       if(!(flag&1))
         Xorriso_result(xorriso,0);
       if(track_no>0)
         num_payload+= lba - last_track_start;
       last_track_start= lba;
       if((toc_entry.control&7)>=4) /* data track */
         is_data= 1;
     }
     burn_session_get_leadout_entry(sessions[session_no], &toc_entry);
     if (toc_entry.extensions_valid & 1) {
       lba= toc_entry.start_lba;
       burn_lba_to_msf(lba, &pmin, &psec, &pframe);
     } else {
       lba= burn_msf_to_lba(pmin, psec, pframe);
       lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec, toc_entry.pframe);
     }
     sprintf(respt, "Media content: session %2d  ", session_no+1);
     sprintf(respt+strlen(respt), "leadout            lba: %9d\n", lba);
     if(!(flag&1))
       Xorriso_result(xorriso,0);
     last_track_size= lba - last_track_start;
     num_payload+= last_track_size;
     if(is_data)
       num_data+= last_track_size;
   }
 }
 if(xorriso->request_to_abort)
   return(1);
 num_wasted= lba - num_payload;
 num_nondata= lba - num_data;
 Sfile_scale(((double) num_data) * 2048.0, mem_text,5,1e4,1);

#ifdef NIX
 sprintf(respt, "Media summary: %d session%s, %d data blocks, %s",
         num_sessions, (num_sessions==1 ? "" : "s"), num_data, mem_text);
 if(num_nondata>0) {
   Sfile_scale(((double) num_nondata) * 2048.0, mem_text,5,1e4,1);
   sprintf(respt+strlen(respt), ", %s non-data", mem_text);
 }
#else
 sprintf(respt, "Media summary: %d session%s, %d data blocks, %s data",
         num_sessions, (num_sessions==1 ? "" : "s"), num_data, mem_text);
 num_free= isoburn_disc_available_space(drive, NULL); 
 Sfile_scale((double) num_free, mem_text,5,1e4,1);
 sprintf(respt+strlen(respt), ", %s free", mem_text);
#endif

 sprintf(respt+strlen(respt), "\n");
 Xorriso_result(xorriso,0);

 if (s == BURN_DISC_APPENDABLE && nwa!=0) {
   ret= isoburn_disc_track_lba_nwa(drive, NULL, 0, &lba, &nwa);
   if(ret>0) {
     sprintf(respt, "Media nwa    : %ds\n", nwa);
     if(!(flag&1))
       Xorriso_result(xorriso,0);
   }
 }

 if (disc!=NULL)
   burn_disc_free(disc);
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


int Xorriso_show_devices(struct XorrisO *xorriso, int flag)
{
 char adr[BURN_DRIVE_ADR_LEN];
 int i;
 struct burn_drive_info *drive_list= NULL;
 unsigned int drive_count;
 char *respt, perms[8];
 struct stat stbuf;

 sprintf(xorriso->info_text, "Beginning to scan for devices ...\n");
 Xorriso_info(xorriso,0);

 burn_drive_clear_whitelist(); 
 while(!burn_drive_scan(&drive_list, &drive_count)) {
   Xorriso_process_msg_queues(xorriso,0);
   usleep(100000);
 }
 Xorriso_process_msg_queues(xorriso,0);
 if(drive_count <= 0) {

   /* >>> was a drive_list created at all ? */
   /* >>> must it be freed ? */

   sprintf(xorriso->info_text, "No drives found");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0);
   return(0);
 }
 sprintf(xorriso->info_text, "Full drive scan done\n");
 Xorriso_info(xorriso,0);

 respt= xorriso->result_line;
 for(i= 0; i < drive_count && !(xorriso->request_to_abort); i++) {
   if(burn_drive_get_adr(&(drive_list[i]), adr)<=0)
     strcpy(adr, "-get_adr_failed-");
   Xorriso_process_msg_queues(xorriso,0);
   if(stat(adr,&stbuf)==-1) {
     sprintf(perms,"errno=%d",errno);
   } else { 
     strcpy(perms,"------");
     if(stbuf.st_mode&S_IRUSR) perms[0]= 'r';
     if(stbuf.st_mode&S_IWUSR) perms[1]= 'w';
     if(stbuf.st_mode&S_IRGRP) perms[2]= 'r';
     if(stbuf.st_mode&S_IWGRP) perms[3]= 'w';
     if(stbuf.st_mode&S_IROTH) perms[4]= 'r';
     if(stbuf.st_mode&S_IWOTH) perms[5]= 'w';
   }
   sprintf(respt, "%d  -dev '%s' %s :  '%-8.8s' '%s' \n",
           i, adr, perms, drive_list[i].vendor, drive_list[i].product);
   Xorriso_result(xorriso,0);
 }
 burn_drive_info_free(drive_list);
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


int Xorriso_tell_media_space(struct XorrisO *xorriso,
                             int *media_space, int *free_space, int flag)
{
 int ret;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 struct burn_write_opts *burn_options;

 (*free_space)= (*media_space)= 0;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to -tell_media_space", 2);
 if(ret<=0)
   return(0);

 ret= Xorriso_make_write_options(xorriso, drive, &burn_options, 0);
 if(ret<=0)
   return(-1);
 (*free_space)= (*media_space)=
              isoburn_disc_available_space(drive, burn_options) / (off_t) 2048;
 burn_write_opts_free(burn_options);

 if(xorriso->volset_change_pending) {
   ret= Xorriso_write_session(xorriso, 1);
   if(ret>0)
     (*free_space)-= ret;
 }
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


/* @param flag bit0=fast , bit1=deformat
   @return 0=failure, did not touch media , -1=failure, altered media
           1=success, altered media       ,  2=success, did not touch media
*/
int Xorriso_blank_media(struct XorrisO *xorriso, int flag)
{
 int ret, do_deformat= 0;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 enum burn_disc_status disc_state;
 struct burn_progress p;
 double percent = 1.0;
 int current_profile;
 char current_profile_name[80];
 char mode_names[4][80]= {"all", "fast", "deformat", "deformat_quickest"};

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to -blank", 2);
 if(ret<=0)
   return(0);

 burn_disc_get_profile(drive, &current_profile, current_profile_name);

 /* >>> */;

 disc_state = isoburn_disc_get_status(drive);
 if(current_profile == 0x13) { /* overwriteable DVD-RW */
   /* Depending on flag bit1 formatted DVD-RW will get blanked to sequential
      state or pseudo blanked by invalidating an eventual ISO image. */
   if(flag&2)
     do_deformat= 1;
 } else if(current_profile == 0x14) { /* sequential DVD-RW */
   if((flag&1) && !(flag&2)) {
     sprintf(xorriso->info_text,
             "-blank: DVD-RW present. Mode 'fast' defaulted to mode 'all'.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     sprintf(xorriso->info_text,
             "Mode 'deformat_quickest' produces single-session-only media.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0);
     flag&= ~1;
   }
 } else if(disc_state == BURN_DISC_BLANK) {
   sprintf(xorriso->info_text,"Blank media detected. Will leave it untouched");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   return 2;
 } else if(disc_state==BURN_DISC_FULL || disc_state==BURN_DISC_APPENDABLE) {
   ;
 } else if(disc_state == BURN_DISC_EMPTY) {
   sprintf(xorriso->info_text,"No media detected in drive");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return 0;
 } else {
   sprintf(xorriso->info_text, "Unsuitable drive and media state");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return 0;
 }
 if(!isoburn_disc_erasable(drive)) {
   sprintf(xorriso->info_text, "Media is not of erasable type");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return 0;
 }
 if(xorriso->do_dummy) {
   sprintf(xorriso->info_text,
           "-dummy mode prevents blanking of media in mode '%s'.",
           mode_names[flag&3]);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   return(1);
 }
 sprintf(xorriso->info_text, "Beginning to blank media in mode '%s'.\n",
         mode_names[flag&3]);
 Xorriso_info(xorriso,0);

 if(do_deformat)
   burn_disc_erase(drive, (flag&1));
 else
   isoburn_disc_erase(drive, (flag&1));
 usleep(1000000);
 while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) {
   if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */
     percent = 1.0 + ((double) p.sector+1.0) / ((double) p.sectors) * 98.0;
   sprintf(xorriso->info_text, "Blanking  ( %.1f%% done )", percent);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0);
   usleep(1000000);
 }
 Xorriso_process_msg_queues(xorriso,0);
 sprintf(xorriso->info_text, "Blanking done\n");
 Xorriso_info(xorriso,0);
 return(1);
}

/* @return 0=failure, did not touch media , -1=failure, altered media
           1=success, altered media       ,  2=success, did not touch media
*/
int Xorriso_format_media(struct XorrisO *xorriso, int flag)
{
 int ret, mode_flag= 0;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 int current_profile;
 char current_profile_name[80];

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to -format", 2);
 if(ret<=0)
   return(0);

 burn_disc_get_profile(drive, &current_profile, current_profile_name);
 if(current_profile == 0x14) {
   ; /* ok DVD-RW sequential */
 } else if(current_profile == 0x1a) {
   mode_flag= 2;
 } else {
   sprintf(xorriso->info_text,
          "Can only -format DVD+RW and sequential DVD-RW");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   sprintf(xorriso->info_text,"Media current: %s (%4.4xh)",
           current_profile_name, current_profile);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   return 0;
 }

 if(xorriso->do_dummy) {
   sprintf(xorriso->info_text, "-dummy mode prevents formatting of media.");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   return(1);
 }
 sprintf(xorriso->info_text, "Beginning to format media.\n");
 Xorriso_info(xorriso, 0);
 burn_disc_format(drive, (off_t) 0, mode_flag);

 ret= Xorriso_pacifier_loop(xorriso, drive, 0);
 if(ret<=0)
   return(ret);

 Xorriso_process_msg_queues(xorriso,0);
 sprintf(xorriso->info_text, "Formatting done\n");
 Xorriso_info(xorriso,0);
 return(1);
}


int Xorriso_cannot_create_iter(struct XorrisO *xorriso, int iso_error,int flag)
{
 Xorriso_process_msg_queues(xorriso,0);
 Xorriso_report_iso_error(xorriso, "", iso_error, "Cannot create iter", 0,
                          "FATAL", 1);
 sprintf(xorriso->info_text, "Cannot create IsoDirIter object");
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
 return(1);
}


int Xorriso__node_lba_cmp(const void *node1, const void *node2)
{
 uint32_t lba1= 0, lba2= 0;
 int ret;

 ret = iso_node_get_old_image_lba(*((IsoNode **) node1), &lba1, 0);
 if(ret!=1)
   lba1= 0;
 ret = iso_node_get_old_image_lba(*((IsoNode **) node2), &lba2, 0);
 if(ret!=1)
   lba1= 0;
 return(lba1-lba2);  
}

/* The caller shall make no assumptions about the meaning of iter, node_array,
   node_count, node_idx ! They are just opaque handles for which the caller
   provides the memory of proper type.
   @param flag bit0= initialize iteration
               bit1= action needs full freedom of object manipulation
               bit2= action needs LBA sorted iteration
               bit31= end iteration (mandatory !)
*/
int Xorriso_findi_iter(struct XorrisO *xorriso, IsoDir *dir_node, off_t *mem,
                       IsoDirIter **iter, 
                       IsoNode ***node_array, int *node_count, int *node_idx,
                       IsoNode **iterated_node, int flag)
{
 int ret, i;
 IsoNode *node;
 off_t new_mem= 0;
 char mem_text[80], limit_text[80];

 if(flag&1) {
   *node_array= NULL;
   *node_count= -1;
   *node_idx= 0;
   *iter= NULL;
   ret= iso_dir_get_children(dir_node, iter);
   if(ret<0) {
cannot_iter:;
     Xorriso_cannot_create_iter(xorriso, ret, 0);
     return(-1);
   }
   if((flag&2)|(flag&4)) {
     /* copy list of nodes and prepare soft iterator */
     *node_count= 0;
     while(iso_dir_iter_next(*iter, &node) == 1)
       (*node_count)++;
     iso_dir_iter_free(*iter);
     *iter= NULL;

     new_mem= ((*node_count)+1) * sizeof(IsoNode *);
     if(new_mem > xorriso->temp_mem_limit) {
       Sfile_scale((double) new_mem, mem_text, 5,1e4, 0);
       Sfile_scale((double) xorriso->temp_mem_limit, limit_text, 5,1e4, 0);
       sprintf(xorriso->info_text,
           "Stacked directory snapshots exceed -temp_mem_limit (%s > %s)",
           mem_text, limit_text);
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       *node_count= -1;
       return(-1);
     }
     (*node_array)= (IsoNode **) calloc((*node_count)+1, sizeof(IsoNode *));
     if(*node_array == NULL) {
       sprintf(xorriso->info_text,
               "Could not allocate inode list of %.f bytes",
               ((double) (*node_count)+1) * (double) sizeof(IsoNode *));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
       *node_count= -1;
       return(-1);
     }
     *mem= new_mem;
     ret= iso_dir_get_children(dir_node, iter);
     if(ret<0)
       goto cannot_iter;
     while(iso_dir_iter_next(*iter, &node) == 1 && *node_idx < *node_count) {
       (*node_array)[*node_idx]= node;
       iso_node_ref(node);
       (*node_idx)++;
     }
     iso_dir_iter_free(*iter);
     *iter= NULL;
     *node_count= *node_idx;
     *node_idx= 0;
     if((flag&4) && *node_count>1)
       qsort(*node_array, *node_count, sizeof(IsoNode *),
             Xorriso__node_lba_cmp);
   }
 }

 if(flag&(1<<31)) {
   if(*node_count>=0 && *node_array!=NULL) {
     for(i= 0; i<*node_count; i++)
       iso_node_unref((*node_array)[i]);
     free(*node_array);
     *node_array= NULL;
     *node_count= -1;
     *node_idx= 0;
   } else {
     if(*iter!=NULL)
       iso_dir_iter_free(*iter);
     *iter= NULL;
   }
 }

 if(flag&(1|(1<<31))) 
   return(1);
 if(*node_count>=0) {
   /* return next node_array element */
   if(*node_idx>=*node_count)
     return(0);
   *iterated_node= (*node_array)[*node_idx];
   (*node_idx)++;
 } else {
   ret= iso_dir_iter_next(*iter, iterated_node);
   return(ret == 1);
 }
 return(1);
}


/* @param boss_iter  If not NULL then this is an iterator suitable for
               iso_dir_iter_remove()  which is then to be used instead
               of iso_node_remove().
   @param flag bit0= remove whole sub tree: rm -r
               bit1= remove empty directory: rmdir  
               bit2= recursion: do not reassure in mode 2 "tree"
               bit3= this is for overwriting and not for plain removal
               bit4= count deleted files in xorriso->pacifier_count
               bit5= with bit0 only remove directory content, not the directory
   @return   <=0 = error
               1 = removed simple node 
               2 = removed directory or tree
               3 = did not remove on user revocation
*/
int Xorriso_rmi(struct XorrisO *xorriso, void *boss_iter, off_t boss_mem,
                char *path, int flag)
{
 int ret, is_dir= 0, pl, not_removed= 0, fret;
 IsoNode *victim_node, *node;
 IsoDir *boss_node, *root_dir;
 IsoDirIter *iter= NULL;
 IsoImage *volume;
 char *sub_name, *name;
 char *sfe= NULL, *sub_path= NULL;
 off_t mem;
 IsoNode **node_array= NULL;
 int node_count, node_idx;

#ifdef Xorriso_fat_local_meM
 char sfe[5*SfileadrL], sub_path[2*SfileadrL];
#else

 /* Avoiding large local memory objects in order to save stack space */
 sfe= malloc(5*SfileadrL);
 sub_path= malloc(2*SfileadrL);
 if(sfe==NULL || sub_path==NULL) {
   Xorriso_no_malloc_memory(xorriso, &sfe, 0);
   {ret= -1; goto ex;}
 }
#endif /* ! Xorriso_fat_local_meM */

#ifndef Libisofs_iso_dir_iter_sufficienT
 /* Ticket 127: A80301 - A80302
    I do not not deem IsoDirIter safe for node list manipulations.
    The parameter boss_iter once was intended to allow such but
    has now been downgraded to a mere check for eventual programming bugs.
 */
 if(boss_iter!=NULL) {
   sprintf(xorriso->info_text,
       "Program error: Xorriso_rmi() was requested to delete iterated node %s",
       Text_shellsafe(path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   ret= -1; goto ex;
 }
#endif /* Libisofs_iso_dir_iter_sufficienT */

 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   goto ex;
 
 if(Xorriso_much_too_long(xorriso, strlen(path), 0)<=0)
   {ret= 0; goto ex;}
 ret= Xorriso_node_from_path(xorriso, volume, path, &victim_node, 0);
 if(ret<=0)
   goto ex;
 root_dir= iso_image_get_root(volume);
 if(((void *) root_dir) == ((void *) victim_node)) {
   sprintf(xorriso->info_text, "May not delete root directory");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   {ret= 0; goto ex;}
 }

 if(LIBISO_ISDIR(victim_node))
   is_dir= 1;
 if(!is_dir) {
   if(flag&2) { /* rmdir */
     sprintf(xorriso->info_text, "%s in loaded ISO image is not a directory",
             Text_shellsafe(path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }
 } else {
   if(flag&1) { /* rm -r */
     if((xorriso->do_reassure==1 && !xorriso->request_not_to_ask) ||
        (flag&32)) {
       /* Iterate over subordinates and delete them */
       mem= boss_mem;

       ret= Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem,
                         &iter, &node_array, &node_count, &node_idx,
                         &node, 1|2);
       if(ret<=0) {
cannot_create_iter:;
         Xorriso_cannot_create_iter(xorriso, ret, 0);
         ret= -1; goto ex;
       }
       pl= strlen(path);
       strcpy(sub_path, path);
       if(pl==0 || sub_path[pl-1]!='/') {
         sub_path[pl++]= '/';
         sub_path[pl]= 0;
       }
       sub_name= sub_path+pl;
       while(1) { 
         ret= Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem, &iter,
                                &node_array, &node_count, &node_idx, &node, 0);
         if(ret<0)
           goto ex;
         if(ret==0 || xorriso->request_to_abort)
       break;
         name= (char *) iso_node_get_name(node);
         if(Xorriso_much_too_long(xorriso, pl+1+strlen(name), 0)<=0)
           {ret= 0; goto rm_r_problem_handler;}
         strcpy(sub_name, name);
         ret= Xorriso_rmi(xorriso, iter, mem, sub_path, (flag&(1|2|8|16))|4);
         if(ret==3 || ret<=0 || xorriso->request_to_abort) {
rm_r_problem_handler:;
           not_removed= 1;
           fret= Xorriso_eval_problem_status(xorriso, ret, 1|2);
           if(fret<0)
             goto dir_not_removed;
         }
       }
       if(flag&32)
         {ret= 2; goto ex;}

       if(not_removed) {
dir_not_removed:;
         sprintf(xorriso->info_text, "Directory not removed: %s",
                 Text_shellsafe(path, sfe, 0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
         if(ret>0)
           ret= 3;
         goto ex;
       }
     }
   } else {
     if(!(flag&2)) { /* not rmdir */
       sprintf(xorriso->info_text, "%s in loaded ISO image is a directory",
               Text_shellsafe(path, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       ret= 0; goto ex;
     }

     ret= iso_dir_get_children((IsoDir *) victim_node, &iter);
     Xorriso_process_msg_queues(xorriso,0);
     if(ret<0)
       goto cannot_create_iter;
     if(ret>0) {
       if(iso_dir_iter_next(iter, &node) == 1) {
         sprintf(xorriso->info_text,
                 "Directory not empty on attempt to delete: %s",
                 Text_shellsafe(path, sfe, 0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         ret= 0; goto ex;
       }
     }
   }
 }

 if(xorriso->request_to_abort)
   {ret= 3; goto ex;}
 boss_node= iso_node_get_parent(victim_node);
 Xorriso_process_msg_queues(xorriso,0);
 if(boss_node==NULL) {
   sprintf(xorriso->info_text,
           "Cannot find parent node of %s in loaded ISO image",
           Text_shellsafe(path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }

 while((xorriso->do_reassure==1 || (xorriso->do_reassure==2 && !(flag&4)))
       && !xorriso->request_not_to_ask) {
   /* ls -ld */
   Xorriso_ls_filev(xorriso, xorriso->wdi, 1, &path, (off_t) 0, 1|2|8);
   if(is_dir) /* du -s */
     Xorriso_ls_filev(xorriso, xorriso->wdi, 1, &path, (off_t) 0, 2|4);
   if(flag&8)
     sprintf(xorriso->info_text,
  "File exists. Remove ?  n= keep old, y= remove, x= abort, @= stop asking\n");
   else
     sprintf(xorriso->info_text,
  "Remove above file ?  n= keep it, y= remove it, x= abort, @= stop asking\n");
   Xorriso_info(xorriso, 4);
   ret= Xorriso_request_confirmation(xorriso, 1|2|4|16);
   if(ret<=0)
     goto ex;
   if(xorriso->request_to_abort) {
     sprintf(xorriso->info_text,
             "Removal operation aborted by user before file: %s",
             Text_shellsafe(path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     ret= 3; goto ex;
   }
   if(ret==3)
 continue;
   if(ret==6) /* yes */
 break;
   if(ret==4) { /* yes, do not ask again */
     xorriso->request_not_to_ask= 1;
 break;
   }
   if(ret==1) { /* no */
     sprintf(xorriso->info_text, "Kept in existing state: %s",
             Text_shellsafe(path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     ret= 3; goto ex;
   }
 }

#ifdef Libisofs_iso_dir_iter_sufficienT

 if(boss_iter!=NULL) {
   ret= iso_dir_iter_remove((IsoDirIter *) boss_iter);
   if(ret<0)
     ret= -1;
 } else
   ret= iso_node_remove(victim_node);

#else /* ! Libisofs_iso_dir_iter_sufficienT */

 ret= iso_node_remove(victim_node);

#endif /* Libisofs_iso_dir_iter_sufficienT */

 Xorriso_process_msg_queues(xorriso,0);
 if(ret<0) {
   Xorriso_report_iso_error(xorriso, path, ret, "Cannot remove node", 0,
                            "FATAL", 1);
   sprintf(xorriso->info_text,
           "Internal failure to remove %s from loaded ISO image",
           Text_shellsafe(path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   ret= -1; goto ex;
 }
 if(flag&16)
   xorriso->pacifier_count++;
 xorriso->volset_change_pending= 1;
 ret= 1+!!is_dir;
ex:;

#ifndef Xorriso_fat_local_meM
 if(sfe!=NULL)
   free(sfe);
 if(sub_path!=NULL)
   free(sub_path);
#endif /* ! Xorriso_fat_local_meM */

 Xorriso_findi_iter(xorriso, (IsoDir *) victim_node, &mem, &iter,
                    &node_array, &node_count, &node_idx, &node, (1<<31));
 return(ret);
} 


int Xorriso__node_name_cmp(const void *node1, const void *node2)
{
 char *name1, *name2;

 name1= (char *) iso_node_get_name(*((IsoNode **) node1));
 name2= (char *) iso_node_get_name(*((IsoNode **) node2));
 return(strcmp(name1,name2));
}


/* @param flag bit0= only accept directory nodes
               bit1= do not report memory usage as DEBUG
               bit2= do not apply search pattern but accept any node
*/
int Xorriso_sorted_node_array(struct XorrisO *xorriso, 
                              IsoDir *dir_node,
                              int *nodec, IsoNode ***node_array,
                              off_t boss_mem, int flag)
{
 int i, ret, failed_at;
 char *npt;
 IsoDirIter *iter= NULL;
 IsoNode *node;
 off_t mem;

 mem= ((*nodec)+1)*sizeof(IsoNode *);
 ret= Xorriso_check_temp_mem_limit(xorriso, mem+boss_mem, flag&2);
 if(ret<=0)
   return(ret);

 *node_array= calloc(sizeof(IsoNode *), (*nodec)+1);
 if(*node_array==NULL) {
   sprintf(xorriso->info_text,
           "Cannot allocate memory for %d directory entries", *nodec); 
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }

 ret= iso_dir_get_children(dir_node, &iter);
 if(ret<0) {
   Xorriso_cannot_create_iter(xorriso, ret, 0);
   return(-1);
 }
 
 for(i= 0; iso_dir_iter_next(iter, &node) == 1 && i<*nodec; ) {
   npt= (char *) iso_node_get_name(node);
   if(!(flag&4)) {
     ret= Xorriso_regexec(xorriso, npt, &failed_at, 0);
     if(ret)
 continue; /* no match */
   }
   if(flag&1)
     if(!LIBISO_ISDIR(node))
 continue;
   (*node_array)[i++]= node;
 }
 iso_dir_iter_free(iter);
 *nodec= i;
 if(*nodec<=0)
   return(1);
 qsort(*node_array, *nodec, sizeof(IsoNode *), Xorriso__node_name_cmp);
 return(1);
}


/* @param flag bit0= do not only sum up sizes but also print subdirs
*/
int Xorriso_show_du_subs(struct XorrisO *xorriso, IsoDir *dir_node,
                      char *abs_path, char *rel_path, off_t *size,
                      off_t boss_mem, int flag)
{
 int i, ret, no_sort= 0, filec= 0, l;
 IsoDirIter *iter= NULL;
 IsoNode *node, **node_array= NULL;
 char *name;
 off_t sub_size, report_size, mem= 0;

#ifdef Xorriso_fat_local_meM
 char path[SfileadrL], show_path[SfileadrL], sfe[5*SfileadrL];
#else /* Xorriso_fat_local_meM */
 char *path= NULL, *show_path= NULL, *sfe= NULL;

 sfe= malloc(5*SfileadrL);
 path= malloc(SfileadrL);
 show_path= malloc(SfileadrL);
 if(path==NULL || show_path==NULL || sfe==NULL) {
   Xorriso_no_malloc_memory(xorriso, &sfe, 0);
   {ret= -1; goto ex;}
 }

#endif /* ! Xorriso_fat_local_meM */

 *size= 0;
 ret= iso_dir_get_children(dir_node, &iter);
 if(ret<0) {
cannot_create_iter:;
   Xorriso_cannot_create_iter(xorriso, ret, 0);
   {ret= -1; goto ex;}
 }
 for(i= 0; iso_dir_iter_next(iter, &node) == 1; ) {
   sub_size= 0;
   name= (char *) iso_node_get_name(node);
   strcpy(show_path, rel_path);
   if(Sfile_add_to_path(show_path, name, 0)<=0)
       goto much_too_long;
   if(LIBISO_ISDIR(node)) {
     strcpy(path, abs_path);
     if(Sfile_add_to_path(path, name, 0)<=0) {
much_too_long:;
       Xorriso_much_too_long(xorriso, strlen(path)+strlen(name)+1, 2);
       {ret= -1; goto ex;}
     }
     filec++;
     l= strlen(rel_path)+1;
     mem+= l;
     if(l % sizeof(char *))
       mem+= sizeof(char *)-(l % sizeof(char *));
     if(flag&1) /* diving and counting is done further below */
 continue;
     ret= Xorriso_show_du_subs(xorriso, (IsoDir *) node,
                               path, show_path, &sub_size, boss_mem, 0);
     if(ret<0)
       goto ex;
     if(ret==0)
 continue;
   }

   if(LIBISO_ISREG(node)) {
     sub_size+= iso_file_get_size((IsoFile *) node)+2048;
/*
     sub_size+= iso_file_get_size((IsoFile *) node)+strlen(name)+1;
*/
   }

   if(sub_size>0)
     (*size)+= sub_size;
   Xorriso_process_msg_queues(xorriso,0);
 }

 if(filec<=0 || !(flag&1))
   {ret= 1; goto ex;}

 /* Reset iteration */
 iso_dir_iter_free(iter);
 iter= NULL;
 Xorriso_process_msg_queues(xorriso,0);

 ret= Xorriso_sorted_node_array(xorriso, dir_node, &filec, &node_array,
                                boss_mem, 1|2|4);
 if(ret<0)
   goto ex;
 if(ret==0) {
   no_sort= 1;
   ret= iso_dir_get_children(dir_node, &iter);
   if(ret<0)
     goto cannot_create_iter;
 }

 for(i= 0; (no_sort || i<filec) && !(xorriso->request_to_abort); i++) {
   if(no_sort) {
     ret= iso_dir_iter_next(iter, &node);
     if(ret!=1)
 break;
     if(!LIBISO_ISDIR(node))
 continue;
   } else
     node= node_array[i];

   sub_size= 0;
   name= (char *) iso_node_get_name(node);
   strcpy(show_path, rel_path);
   if(Sfile_add_to_path(show_path, name, 0)<=0)
     goto much_too_long;
   strcpy(path, abs_path);
   if(Sfile_add_to_path(path, name, 0)<=0)
     goto much_too_long;
   ret= Xorriso_show_du_subs(xorriso, (IsoDir *) node,
                             path, show_path, &sub_size, boss_mem+mem, flag&1);
   if(ret<0)
     goto ex;

   if(LIBISO_ISREG(node)) {
     sub_size+= iso_file_get_size((IsoFile *) node)+2048;
/*
     sub_size+= iso_tree_node_get_size((IsoFile *) node)+strlen(name)+1;
*/
   }
   if(sub_size>0)
     (*size)+= sub_size;
   report_size= sub_size/1024;
   if(report_size*1024<sub_size)
      report_size++;
   sprintf(xorriso->result_line, "%7.f ",(double) (report_size));
   sprintf(xorriso->result_line+strlen(xorriso->result_line), "%s\n",
           Text_shellsafe(show_path, sfe, 0));
   Xorriso_result(xorriso, 0);
 }
 ret= 1;
ex:;

#ifndef Xorriso_fat_local_meM
 if(sfe!=NULL)
   free(sfe);
 if(path!=NULL)
   free(path);
 if(show_path!=NULL)
   free(show_path);
#endif /* ! Xorriso_fat_local_meM */

 if(iter!=NULL)
   iso_dir_iter_free(iter);
 if(node_array!=NULL)
   free((char *) node_array);
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


/* @param flag bit0= *node is already valid
               bit1= add extra block for size estimation
*/
int Xorriso_fake_stbuf(struct XorrisO *xorriso, char *path, struct stat *stbuf,
                       IsoNode **node, int flag)
{
 int ret;
 IsoImage *volume;

 memset((char *) stbuf, 0, sizeof(struct stat));
 if(!(flag&1)) {
   ret= Xorriso_get_volume(xorriso, &volume, 0);
   if(ret<=0)
     return(-1);
   ret= Xorriso_node_from_path(xorriso, volume, path, node, 1);
   if(ret<=0)
     *node= NULL;
 }
 if(*node==NULL)
   return(0);

 /* >>> stbuf->st_dev */
 /* >>> stbuf->st_ino */

 stbuf->st_mode= iso_node_get_permissions(*node) & 07777;
 if(LIBISO_ISDIR(*node))
   stbuf->st_mode|= S_IFDIR;
 else if(LIBISO_ISREG(*node))
   stbuf->st_mode|= S_IFREG;
 else if(LIBISO_ISLNK(*node))
   stbuf->st_mode|= S_IFLNK;
 else if(LIBISO_ISLNK(*node))
   stbuf->st_mode|= S_IFCHR;
 else if(LIBISO_ISBLK(*node))
   stbuf->st_mode|= S_IFBLK;
 else if(LIBISO_ISFIFO(*node))
   stbuf->st_mode|= S_IFIFO;
 else if(LIBISO_ISSOCK(*node))
   stbuf->st_mode|= S_IFSOCK;

 /* >>> NG  How to represent LIBISO_BOOT ? */


 /* >>> With directories this should be : number of subdirs + 2 */
 /* >>> ??? How to obtain RR hardlink number for other types ? */
 stbuf->st_nlink= 1;

 stbuf->st_uid= iso_node_get_uid(*node);
 stbuf->st_gid= iso_node_get_gid(*node);

 /* >>> stbuf->st_rdev */

 if(LIBISO_ISREG(*node))
   stbuf->st_size= iso_file_get_size((IsoFile *) *node)+ (2048 * !!(flag&2));
 else
   stbuf->st_size= 0;

 stbuf->st_blksize= 2048;
 stbuf->st_blocks= stbuf->st_size / (off_t) 2048;
 if(stbuf->st_blocks * (off_t) 2048 != stbuf->st_size)
   stbuf->st_blocks++; 

 stbuf->st_atime= iso_node_get_atime(*node);
 stbuf->st_mtime= iso_node_get_mtime(*node);
 stbuf->st_ctime= iso_node_get_ctime(*node);
 return(1);
}


int Xorriso_iso_lstat(struct XorrisO *xorriso, char *path, struct stat *stbuf,
                      int flag)
{
 int ret;
 IsoNode *node;

 ret= Xorriso_fake_stbuf(xorriso, path, stbuf, &node, 0);
 if(ret>0)
   return(0);
 return(-1);
}


int Xorriso_sorted_dir_i(struct XorrisO *xorriso, IsoDir *dir_node,
                         int *filec, char ***filev, off_t boss_mem, int flag)
{
 int i,j,ret;
 IsoDirIter *iter= NULL;
 IsoNode *node;
 char *name;
 off_t mem;

 (*filec)= 0;
 (*filev)= NULL;

 ret= iso_dir_get_children(dir_node, &iter);
 if(ret<0) {
cannot_iter:;
   Xorriso_cannot_create_iter(xorriso, ret, 0);
   {ret= -1; goto ex;}
 }
 mem= 0;
 for(i= 0; iso_dir_iter_next(iter, &node) == 1; ) {
   name= (char *) iso_node_get_name(node);
   mem+= sizeof(char *)+strlen(name)+8;
   (*filec)++;
 }
 iso_dir_iter_free(iter);
 iter= NULL;
 if(*filec==0)
   {ret= 1; goto ex;}

 ret= Xorriso_check_temp_mem_limit(xorriso, mem+boss_mem, 2);
 if(ret<=0)
   goto ex;
 (*filev)= (char **) calloc(*filec, sizeof(char *));
 if(*filev==NULL)
   {ret= -1; goto ex; }
 ret= iso_dir_get_children(dir_node, &iter);
 if(ret<0) 
   goto cannot_iter;
 for(i= 0; i<*filec; i++) {
   ret= iso_dir_iter_next(iter, &node);
   if(ret!=1)
 break;
   name= (char *) iso_node_get_name(node);
   (*filev)[i]= strdup(name);
   if((*filev)[i]==NULL) {
     for(j= 0; j<i; j++)
       if((*filev)[j]!=NULL)
         free((*filev)[j]);
     free((char *) (*filev));
     ret= -1; goto ex;
   }
 }
 Sort_argv(*filec, *filev, 0);
 ret= 1;
ex:;
 if(iter!=NULL)
   iso_dir_iter_free(iter);
 return(ret);
}


/* @param flag bit0= long format
               bit1= do not print count of nodes
               bit2= du format
               bit3= print directories as themselves (ls -d)
*/
int Xorriso_ls_filev(struct XorrisO *xorriso, char *wd,
                     int filec, char **filev, off_t boss_mem, int flag)
{
 int i, ret, was_error= 0, dfilec= 0, pass, passes;
 IsoNode *node;
 IsoImage *volume;
 char sfe[5*SfileadrL], sfe2[5*SfileadrL], path[SfileadrL];
 char link_target[SfileadrL], *rpt, **dfilev= NULL;
 off_t size;
 struct stat stbuf;

 rpt= xorriso->result_line;

 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);

 Sort_argv(filec, filev, 0);

 /* Count valid nodes, warn of invalid ones */
 for(i= 0; i<filec; i++) {
   ret= Xorriso_make_abs_adr(xorriso, wd, filev[i], path, 1|2|4);
   if(ret<=0) {
     was_error++;
 continue;
   }
   ret= Xorriso_node_from_path(xorriso, volume, path, &node, 1);
   if(ret<=0) {
     sprintf(xorriso->info_text, "Not found in ISO image: %s",
             Text_shellsafe(path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0);
     was_error++;
 continue;
   }
 }

 if((flag&8) && !(flag&(2|4))) {
   sprintf(xorriso->info_text, "Valid ISO nodes found: %d\n", filec-was_error);
   Xorriso_info(xorriso,1);
   if(filec-was_error<=0)
     return(!was_error);
 }

 passes= 1+!(flag&(4|8));
 for(pass= 0; pass<passes; pass++)
 for(i= 0; i<filec && !(xorriso->request_to_abort); i++) {
   rpt[0]= 0;
   ret= Xorriso_make_abs_adr(xorriso, wd, filev[i], path, 1|2|4);
   if(ret<=0)
 continue;
   ret= Xorriso_fake_stbuf(xorriso, path, &stbuf, &node, (flag&4)>>1);
   if(ret<=0)
 continue;
   if(LIBISO_ISDIR(node)  && !(flag&(4|8))) {
     if(pass==0)
 continue;
     if(filec>1) {
       strcpy(xorriso->result_line, "\n");
       Xorriso_result(xorriso,0);
       sprintf(xorriso->result_line, "%s:\n", Text_shellsafe(filev[i], sfe,0));
       Xorriso_result(xorriso,0);
     }
     ret= Xorriso_sorted_dir_i(xorriso, 
                               (IsoDir *) node, &dfilec, &dfilev, boss_mem, 0);
     if(ret<=0) {

       /* >>> libisofs iterator loop and single item Xorriso_lsx_filev() */;

     } else {
       if(flag&1) {
         sprintf(xorriso->result_line, "total %d\n", dfilec);
         Xorriso_result(xorriso,0);
       }
       Xorriso_ls_filev(xorriso, path,
                        dfilec, dfilev, boss_mem, (flag&1)|2|8);
     }
     if(dfilec>0)
       Sfile_destroy_argv(&dfilec, &dfilev, 0);
 continue;
   } else
     if(pass>0)
 continue;
   link_target[0]= 0;
   if((flag&5)==1) { /* -ls_l */
     ret= Xorriso_format_ls_l(xorriso, &stbuf, 0);
     if(ret<=0)
 continue;
     if(LIBISO_ISLNK(node)) {
       if(Sfile_str(link_target, (char *) iso_symlink_get_dest(
                                                   (IsoSymlink *) node), 0)<=0)
         link_target[0]= 0;
     }
   } else if(flag&4) { /* -du or -dus */
     size= stbuf.st_size;
     if(S_ISDIR(stbuf.st_mode)) {
       ret= Xorriso_show_du_subs(xorriso, (IsoDir *) node,
                                 path, filev[i], &size, boss_mem, flag&1);
       if(ret<0)
         return(-1);
       if(ret==0)
 continue;
     }
     sprintf(rpt, "%7.f ",(double) (size/1024));
   }
   if(link_target[0] && (flag&5)==1)
     sprintf(xorriso->result_line+strlen(xorriso->result_line), "%s -> %s\n",
             Text_shellsafe(filev[i], sfe, 0),
             Text_shellsafe(link_target, sfe2, 0));
   else
     sprintf(xorriso->result_line+strlen(xorriso->result_line), "%s\n",
             Text_shellsafe(filev[i], sfe, 0));
   Xorriso_result(xorriso, 0);
 }
 return(!was_error);
}


/* This function needs less buffer memory than Xorriso_ls_filev() but cannot
   perform structured pattern matching as done by Xorriso_expand_pattern()
   for subsequent Xorriso_ls_filev().
   @param flag bit0= long format
               bit1= only check for directory existence
               bit2= do not apply search pattern but accept any file
               bit3= just count nodes and return number
*/
int Xorriso_ls(struct XorrisO *xorriso, int flag)
{
 int ret, is_dir= 0, i, filec= 0, failed_at, no_sort= 0;
 IsoNode *node, **node_array= NULL;
 IsoDir *dir_node;
 IsoImage *volume;
 IsoDirIter *iter= NULL;
 char sfe[5*SfileadrL], sfe2[5*SfileadrL], link_target[SfileadrL], *npt, *rpt;
 struct stat stbuf;

 rpt= xorriso->result_line;

 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);

 ret= Xorriso_node_from_path(xorriso, volume, xorriso->wdi, &node, 0);
 if(ret<=0)
   goto wdi_is_not_a_dir;
 if(LIBISO_ISDIR(node))
   is_dir= 1;
 if(!is_dir) {
wdi_is_not_a_dir:;
   sprintf(xorriso->info_text,
           "Working directory path does not lead to a directory in ISO image"); 
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }
 if(flag&2)
   {ret= 1; goto ex;}

 dir_node= (IsoDir *) node;
 ret= iso_dir_get_children(dir_node, &iter);
 if(ret<0) {
cannot_create_iter:;
   Xorriso_cannot_create_iter(xorriso, ret, 0);
   {ret= -1; goto ex;}
 }
 Xorriso_process_msg_queues(xorriso,0);

 for(i= 0; iso_dir_iter_next(iter, &node) == 1; ) {
   npt= (char *) iso_node_get_name(node);
   if(!(flag&4)) {
     ret= Xorriso_regexec(xorriso, npt, &failed_at, 0);
     if(ret)
 continue; /* no match */
   }
   filec++;
 }
 /* Reset iteration */
 iso_dir_iter_free(iter);
 iter= NULL;
 Xorriso_process_msg_queues(xorriso,0);
 if(flag&8)
   {ret= filec; goto ex;}
 sprintf(xorriso->info_text, "Valid ISO nodes found: %d\n", filec);
 Xorriso_info(xorriso,1);

 ret= Xorriso_sorted_node_array(xorriso, dir_node, &filec, &node_array, 0,
                                flag&4);
 if(ret<0)
   goto ex;
 if(ret==0) {
   no_sort= 1;
   ret= iso_dir_get_children(dir_node, &iter);
   if(ret<0)
     goto cannot_create_iter;
 }

 for(i= 0; i<filec && !(xorriso->request_to_abort); i++) {
   if(no_sort) {
     ret= iso_dir_iter_next(iter, &node);
     if(ret!=1)
 break;
     npt= (char *) iso_node_get_name(node);
     if(!(flag&4)) {
       ret= Xorriso_regexec(xorriso, npt, &failed_at, 0);
       if(ret)
 continue; /* no match */
     }
   } else
     node= node_array[i];

   npt= (char *) iso_node_get_name(node);
   link_target[0]= 0;
   if(LIBISO_ISLNK(node)) {
     if(Sfile_str(link_target, (char *) iso_symlink_get_dest(
                                                   (IsoSymlink *) node), 0)<=0)
       link_target[0]= 0;
   }
   rpt[0]= 0;
   if(flag&1) {
     ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1);
     if(ret<=0)
 continue;
     ret= Xorriso_format_ls_l(xorriso, &stbuf, 0);
     if(ret<=0)
 continue;
   }
   if(link_target[0] && (flag&1))
     sprintf(xorriso->result_line+strlen(xorriso->result_line), "%s -> %s\n",
             Text_shellsafe(npt, sfe, 0), 
             Text_shellsafe(link_target, sfe2, 0));
   else
     sprintf(xorriso->result_line+strlen(xorriso->result_line), "%s\n",
             Text_shellsafe(npt, sfe, 0));
   Xorriso_result(xorriso, 0);
 }

 ret= 1;
ex:;
 if(iter!=NULL)
   iso_dir_iter_free(iter);
 Xorriso_process_msg_queues(xorriso,0);
 if(node_array!=NULL)
   free((char *) node_array);
 return(ret);
}


/* @param boss_iter Opaque handle to be forwarded to actions in ISO image
                    Set to NULL if calling this function from outside ISO world
*/
int Xorriso_rename(struct XorrisO *xorriso, void *boss_iter,
                   char *origin, char *dest, int flag)
{
 int ret, ol, dest_ret;
 char sfe[5*SfileadrL], eff_dest[SfileadrL], dir_adr[SfileadrL], *cpt;
 char *leafname, eff_origin[SfileadrL], sfe2[5*SfileadrL], *old_leafname;
 IsoImage *volume;
 IsoDir *origin_dir, *dest_dir;
 IsoNode *node, *iso_node;

#ifndef Libisofs_iso_dir_iter_sufficienT
 /* Ticket 127: A80301 - A80302
    I do not not deem IsoDirIter safe for node list manipulations.
    The parameter boss_iter once was intended to allow such but
    has now been downgraded to a mere check for eventual programming bugs.
 */
 if(boss_iter!=NULL) {
   sprintf(xorriso->info_text,
    "Program error: Xorriso_rename() was requested to delete iterated node %s",
       Text_shellsafe(origin, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
#endif /* Libisofs_iso_dir_iter_sufficienT */

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, origin, eff_origin, 0);
 if(ret<=0)
   return(ret);
 dest_ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, dest, eff_dest,1);
 if(dest_ret<0)
   return(dest_ret);
 if(dest_ret==0) { /* obtain eff_dest address despite it does not exist */
   ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, dest, eff_dest, 2);
   if(ret<=0)
     return(ret);
 }

 /* Prevent that destination is a subordinate of origin
    (that would be a black hole plopping out of the universe) */
 ol= strlen(eff_origin);
 if(ol==0) {
   sprintf(xorriso->info_text, "May not rename root directory");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 } else if(strcmp(eff_origin, eff_dest)==0) {
   sprintf(xorriso->info_text, "Ignored attempt to rename %s to itself",
           Text_shellsafe(eff_origin,sfe,0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0);
   return(0);
 } else if(strncmp(eff_origin, eff_dest, ol)==0 &&
           (eff_dest[ol]==0 || eff_dest[ol]=='/')) {
   sprintf(xorriso->info_text,
           "May not rename %s to its own sub address %s",
           Text_shellsafe(eff_origin,sfe,0), Text_shellsafe(eff_dest,sfe2,0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }

 /* Check whether destination exists and may be not overwriteable */
 if(dest_ret==2 && xorriso->do_overwrite!=1) {
   sprintf(xorriso->info_text, "Renaming may not overwrite directory: %s",
           Text_shellsafe(eff_dest, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 } else if (dest_ret==1 && !xorriso->do_overwrite) {
   sprintf(xorriso->info_text, "Renaming may not overwite: %s",
           Text_shellsafe(eff_dest, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 } else if(dest_ret>0) {
   ret= Xorriso_rmi(xorriso, boss_iter, (off_t) 0, eff_dest, 1|8);
   if(ret<=0)
     return(0);
   if(ret==3) {
     sprintf(xorriso->info_text, "User revoked renaming of: %s",
             Text_shellsafe(eff_origin, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     return(0);
   }
 }

 /* Ensure existence of destination directory */
 strcpy(dir_adr, eff_dest);
 cpt= strrchr(dir_adr, '/');
 if(cpt==NULL)
   cpt= dir_adr+strlen(dir_adr);
 *cpt= 0;
 if(dir_adr[0]!=0) {
   ret= Xorriso_graft_in(xorriso, boss_iter, NULL, dir_adr,
                         (off_t) 0, (off_t) 0, 1);
   if(ret<=0)
     return(ret);
 }

 /* Move node */
 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);
 Xorriso_node_from_path(xorriso, volume, dir_adr, &iso_node, 0);
 dest_dir= (IsoDir *) iso_node;
 strcpy(dir_adr, eff_origin);
 cpt= strrchr(dir_adr, '/');
 if(cpt==NULL)
   cpt= dir_adr+strlen(dir_adr);
 *cpt= 0;
 Xorriso_node_from_path(xorriso, volume, dir_adr, &iso_node, 0);
 origin_dir= (IsoDir *) iso_node;
 Xorriso_node_from_path(xorriso, volume, eff_origin, &node, 0);
 if(dest_dir==NULL || origin_dir==NULL || node==NULL) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,
           "Internal error on rename: confirmed node turns out as NULL");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
 ret= iso_node_take(node);
 if(ret<0) {
   Xorriso_process_msg_queues(xorriso,0);
   Xorriso_report_iso_error(xorriso, eff_dest, 0, "Cannot take", 0, "FATAL",1);
   sprintf(xorriso->info_text,
           "Internal error on rename: failed to take node");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
 leafname= strrchr(eff_dest, '/');
 if(leafname==NULL)
   leafname= eff_dest;
 else
   leafname++;
 
 old_leafname= (char *) iso_node_get_name(node);
 if(strcmp(leafname, old_leafname)!=0)
   ret= iso_node_set_name(node, leafname);
 else
   ret= 1;
 if(ret<0) {
   Xorriso_process_msg_queues(xorriso,0);
   Xorriso_report_iso_error(xorriso, eff_dest, 0, "Cannot set name", 0,
                            "FAILURE", 1);
   return(-1);
 }
 Xorriso_process_msg_queues(xorriso,0);
 ret= iso_dir_add_node(dest_dir, node, 0);
 if(ret<0) {
   Xorriso_process_msg_queues(xorriso,0);
   Xorriso_report_iso_error(xorriso, eff_dest, 0, "Cannot add", 0, "FATAL", 1);
   sprintf(xorriso->info_text,
           "Internal error on rename: failed to insert node");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }
 return(1);
}


/* @param flag bit0= do not produce info message on success
   @return 1=success,
           0=was already directory, -1=was other type, -2=other error
*/
int Xorriso_mkdir(struct XorrisO *xorriso, char *path, int flag)
{
 int ret;
 char eff_path[SfileadrL], sfe[5*SfileadrL];

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 1);
 if(ret<0)
   return(-2);
 if(ret>0) {
   sprintf(xorriso->info_text,"-mkdir: Address already existing %s",
          Text_shellsafe(eff_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0,
                       (ret==2 ? "WARNING" : "FAILURE"), 0);
   return(-1+(ret==2));
 }
 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, path, eff_path, 2);
 if(ret<0)
   return(-2);
 ret= Xorriso_graft_in(xorriso, NULL, NULL, eff_path, (off_t) 0, (off_t) 0, 1);
 if(ret<=0)
   return(-2);
 if(!(flag&1)) {
   sprintf(xorriso->info_text, "Created directory in ISO image: %s\n",
           Text_shellsafe(eff_path,sfe,0));
   Xorriso_info(xorriso, 0);
 }
 return(1);
}


/* @param flag bit0= count results rather than storing them
               bit1= this is a recursion
               bit2= prepend wd (automatically done if wd[0]!=0)
*/
int Xorriso_obtain_pattern_files_i(
       struct XorrisO *xorriso, char *wd, IsoDir *dir,
       int *filec, char **filev, int count_limit, off_t *mem,
       int *dive_count, int flag)
{
 int ret, failed_at;
 IsoDirIter *iter= NULL;
 IsoNode *node;
 char *name;

#ifdef Xorriso_fat_local_meM
 char adr[SfileadrL];
#else /* Xorriso_fat_local_meM */
 char *adr= NULL;

 adr= malloc(SfileadrL);
 if(adr==NULL) {
   Xorriso_no_malloc_memory(xorriso, &adr, 0);
   {ret= -1; goto ex;}
 }
#endif /* ! Xorriso_fat_local_meM */


 if(!(flag&2))
   *dive_count= 0;
 else
   (*dive_count)++;
 ret= Xorriso_check_for_root_pattern(xorriso, filec, filev, count_limit,
                                     mem, (flag&1)|2);
 if(ret!=2)
   goto ex;

 ret= iso_dir_get_children(dir, &iter);
 if(ret<0) {
   Xorriso_cannot_create_iter(xorriso, ret, 0);
   {ret= -1; goto ex;}
 }
 while(iso_dir_iter_next(iter, &node) == 1) {
   name= (char *) iso_node_get_name(node);
   ret= Xorriso_make_abs_adr(xorriso, wd, name, adr, flag&4);
   if(ret<=0)
     goto ex;
   ret= Xorriso_regexec(xorriso, adr, &failed_at, 1);
   if(ret) { /* no match */
     if(failed_at <= *dive_count) /* no hope for a match */
 continue;

     if(!LIBISO_ISDIR(node)) {

       /* >>> How to deal with softlinks ? */

 continue;
     }
     /* dive deeper */
     ret= Xorriso_obtain_pattern_files_i(
                          xorriso, adr, (IsoDir *) node,
                          filec, filev, count_limit, mem, dive_count, flag|2);
     if(ret<=0)
       goto ex;
   } else {
     ret= Xorriso_register_matched_adr(xorriso, adr, count_limit,
                                       filec, filev, mem, (flag&1)|2);
     if(ret<=0)
       goto ex;
   }
 } 
 ret= 1;
ex:;

#ifndef Xorriso_fat_local_meM
 if(adr!=NULL)
   free(adr);
#endif /* ! Xorriso_fat_local_meM */

 if(flag&2)
   (*dive_count)--;
 return(ret);
}


/* @param flag bit0= a match count !=1 is a FAILURE event
               bit1= with bit0 tolerate 0 matches if pattern is a constant
*/
int Xorriso_expand_pattern(struct XorrisO *xorriso,
                           int num_patterns, char **patterns, int extra_filec,
                           int *filec, char ***filev, off_t *mem, int flag)
{
 int ret, count= 0, abs_adr= 0, i, was_count, was_filec;
 int nonconst_mismatches= 0, dive_count= 0;
 char sfe[5*SfileadrL];
 IsoImage *volume;
 IsoDir *dir= NULL, *root_dir;
 IsoNode *iso_node;

 *filec= 0;
 *filev= NULL;

 xorriso->search_mode= 3;
 xorriso->structured_search= 1;

 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);
 root_dir= iso_image_get_root(volume);
 if(root_dir==NULL) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,
           "While expanding pattern : Cannot obtain root node of ISO image");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   ret= -1; goto ex;
 }

 for(i= 0; i<num_patterns; i++) {
   ret=  Xorriso_prepare_expansion_pattern(xorriso, patterns[i], 0);
   if(ret<=0)
     return(ret);
   if(ret==2)
     abs_adr= 4;
   if(patterns[i][0]=='/' || abs_adr) {
     dir= root_dir;
     abs_adr= 4;
   } else {
     /* This is done so late to allow the following:
        It is not an error if xorriso->wdi does not exist yet, but one may
        not use it as base for relative address searches.
     */
     ret= Xorriso_node_from_path(xorriso, volume, xorriso->wdi, &iso_node, 1);
     dir= (IsoDir *) iso_node;
     if(ret<=0) {
       Xorriso_process_msg_queues(xorriso,0);
       sprintf(xorriso->info_text,
  "While expanding pattern %s : Working directory does not exist in ISO image",
               Text_shellsafe(patterns[i], sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       ret= 0; goto ex;
     }
     if(!LIBISO_ISDIR((IsoNode *) dir)) {
       sprintf(xorriso->info_text,
           "Working directory path does not lead to a directory in ISO image");
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       ret= 0; goto ex;
     }
   }

   /* count the matches */
   was_count= count;
   ret= Xorriso_obtain_pattern_files_i(xorriso, "", dir, &count, NULL, 0,
                                       mem, &dive_count, 1 | abs_adr);
   if(ret<=0)
     goto ex;
   if(was_count==count && strcmp(patterns[i],"*")!=0 && (flag&3)!=1) {
     count++;
     Xorriso_eval_nonmatch(xorriso, patterns[i], &nonconst_mismatches, mem, 0);
   }
 }

 ret= Xorriso_check_matchcount(xorriso, count, nonconst_mismatches,
                               num_patterns, patterns, (flag&1)|2);
 if(ret<=0)
   goto ex;
 count+= extra_filec;
 mem+= extra_filec*sizeof(char *);
 if(count<=0)
   {ret= 0; goto ex;}
 ret= Xorriso_alloc_pattern_mem(xorriso, *mem, count, filev, 0);
 if(ret<=0)
   goto ex;
 /* now store addresses */
 for(i= 0; i<num_patterns; i++) {
   ret=  Xorriso_prepare_expansion_pattern(xorriso, patterns[i], 0);
   if(ret<=0)
     return(ret);
   if(ret==2)
     abs_adr= 4;

   was_filec= *filec;
   ret= Xorriso_obtain_pattern_files_i(xorriso, "", dir, filec, *filev, count,
                                       mem, &dive_count, abs_adr);
   if(ret<=0)
     goto ex;
   if(was_filec == *filec && strcmp(patterns[i],"*")!=0) {
     (*filev)[*filec]= strdup(patterns[i]);
     if((*filev)[*filec]==NULL) {
       (*mem)= strlen(patterns[i])+1;
       Xorriso_no_pattern_memory(xorriso, *mem, 0);
       ret= -1; goto ex;
     }
     (*filec)++;
   } 
 }
 ret= 1;
ex:;
 if(ret<=0) {
   if(filev!=NULL)
     Sfile_destroy_argv(&count, filev, 0);
   *filec= 0;
 }
 return(ret);
}


int Xorriso_set_st_mode(struct XorrisO *xorriso, char *in_path,
                        mode_t mode_and, mode_t mode_or, int flag)
{
 mode_t mode= 0;
 int ret;
 IsoNode *node;
 char sfe[5*SfileadrL], path[SfileadrL];

 ret= Xorriso_get_node_by_path(xorriso, in_path, path, &node, 0);
 if(ret<=0)
   return(ret);
 mode= iso_node_get_permissions(node);
 mode= (mode & mode_and) | mode_or;
 iso_node_set_permissions(node, mode);
 iso_node_set_ctime(node, time(NULL));
 sprintf(xorriso->info_text,"Permissions now: %-5.5o  %s",
         mode, Text_shellsafe(path, sfe, 0));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 xorriso->volset_change_pending= 1;
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


int Xorriso_set_uid(struct XorrisO *xorriso, char *in_path, uid_t uid,
                    int flag)
{
 int ret;
 IsoNode *node;

 ret= Xorriso_get_node_by_path(xorriso, in_path, NULL, &node, 0);
 if(ret<=0)
   return(ret);
 iso_node_set_uid(node, uid);
 iso_node_set_ctime(node, time(NULL));
 xorriso->volset_change_pending= 1;
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


int Xorriso_set_gid(struct XorrisO *xorriso, char *in_path, gid_t gid,
                    int flag)
{
 int ret;
 IsoNode *node;

 ret= Xorriso_get_node_by_path(xorriso, in_path, NULL, &node, 0);
 if(ret<=0)
   return(ret);
 iso_node_set_gid(node, gid);
 iso_node_set_ctime(node, time(NULL));
 xorriso->volset_change_pending= 1;
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


/* @parm flag  bit0= atime, bit1= ctime, bit2= mtime, bit8=no auto ctime */
int Xorriso_set_time(struct XorrisO *xorriso, char *in_path, time_t t,
                    int flag)
{
 int ret;
 IsoNode *node;

 ret= Xorriso_get_node_by_path(xorriso, in_path, NULL, &node, 0);
 if(ret<=0)
   return(ret);
 if(flag&1)
   iso_node_set_atime(node, t);
 if(flag&2)
   iso_node_set_ctime(node, t);
 if(flag&4)
   iso_node_set_mtime(node, t);
 if(!(flag&(2|256)))
   iso_node_set_ctime(node, time(NULL));
 xorriso->volset_change_pending= 1;
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


/* @param flag bit0= not a command parameter (directory iteration or recursion)
               bit1= do not count deleted files with rm and rm_r
   @return <=0 error,
             1=ok
             2=ok, node has been deleted,
             3=ok, do not dive into directory (e.g. because it is a split file)
*/
int Xorriso_findi_action(struct XorrisO *xorriso, struct FindjoB *job,
                         IsoDirIter *boss_iter, off_t boss_mem,
                         char *abs_path, char *show_path, 
                         IsoNode *node, int depth, int flag)
{
 int ret= 0, type, action= 0, hflag, deleted= 0, no_dive= 0;
 uid_t user= 0;
 gid_t group= 0;
 time_t date= 0;
 mode_t mode_or= 0, mode_and= ~1;
 char *target, sfe[5*SfileadrL], *iso_prefix;
 struct FindjoB *subjob;
 struct stat dir_stbuf;

 action= Findjob_get_action_parms(job, &target, &user, &group,
                                &mode_and, &mode_or, &type, &date, &subjob, 0);
 if(action<0)
   action= 0;

 hflag= 16*!(flag&2);
 if(action==1) { /* rm (including rmdir) */
   ret= Xorriso_fake_stbuf(xorriso, abs_path, &dir_stbuf, &node, 1);
   if(ret>0) {
     if(S_ISDIR(dir_stbuf.st_mode))
       hflag= 2;
     ret= Xorriso_rmi(xorriso, boss_iter, boss_mem, abs_path, hflag);
     deleted= 1;
   }
 } else if(action==2) { /* rm_r */
   ret= Xorriso_rmi(xorriso, boss_iter, boss_mem, abs_path, 1|hflag);
   deleted= 1;
 } else if(action==3) {

   /* >>> mv target */;

 } else if(action==4) { /* chown */
   ret= Xorriso_set_uid(xorriso, abs_path, user, 0);
 } else if(action==5) { /* chgrp */
   ret= Xorriso_set_gid(xorriso, abs_path, group, 0);
 } else if(action==6) { /* chmod */
   ret= Xorriso_set_st_mode(xorriso, abs_path, mode_and, mode_or, 0);
 } else if(action==7) { /* alter_date */
   ret= Xorriso_set_time(xorriso, abs_path, date, type&7);
 } else if(action==8) { /* lsdl */
   ret= Xorriso_ls_filev(xorriso, "", 1, &abs_path, (off_t) 0, 1|2|8);
 } else if(action>=9 && action<=13) { /* actions which have own findjobs */
   Findjob_set_start_path(subjob, abs_path, 0);
   ret= Xorriso_findi(xorriso, subjob, boss_iter, boss_mem, NULL,
                      abs_path, &dir_stbuf, depth, 1);
 } else if(action==14 || action==17) { /* compare , update */
   Findjob_get_start_path(job, &iso_prefix, 0);
   ret= Xorriso_find_compare(xorriso, (void *) boss_iter, abs_path,
                             iso_prefix, target, (action==17)|((flag&1)<<1));
   if(ret==2)
     deleted= 1;
   if(ret==3)
     no_dive= 1;
   if(ret>=0)
     ret= 1;
 } else if(action==16 || action==18) { /* not_in_iso , add_missing */
   ;
 } else { /* includes : 15 in_iso */
   sprintf(xorriso->result_line, "%s\n", Text_shellsafe(show_path, sfe, 0));
   Xorriso_result(xorriso, 0);
   ret= 1;
 }
 if(ret<=0)
   return(ret);
 if(deleted)
   return(2);
 if(no_dive)
   return(3);
 return(1);
}


/* @param flag bit0= recursion
               bit1= do not count deleted files with rm and rm_r
   @return <=0 error, 1= ok , 2= dir node and path has been deleted
*/
int Xorriso_findi(struct XorrisO *xorriso, struct FindjoB *job,
                  void *boss_iter, off_t boss_mem,
                  void *dir_node_generic, char *dir_path,
                  struct stat *dir_stbuf, int depth, int flag)
{
 int ret, action= 0, hflag, deleted= 0, no_dive= 0;
 IsoDirIter *iter= NULL;
 IsoDir *dir_node= NULL;
 IsoNode *node, *iso_node;
 IsoImage *volume;
 struct stat stbuf;
 char *name;
 off_t mem;
 IsoNode **node_array= NULL;
 int node_count, node_idx;
#ifdef Xorriso_fat_local_meM
 char path[SfileadrL], abs_path[SfileadrL];
#else /* Xorriso_fat_local_meM */
 char *path= NULL, *abs_path= NULL;

 path= malloc(SfileadrL);
 abs_path= malloc(SfileadrL);
 if(path==NULL || abs_path==NULL) {
   Xorriso_no_malloc_memory(xorriso, &path, 0);
   {ret= -1; goto ex;}
 }
#endif /* ! Xorriso_fat_local_meM */

 action= Findjob_get_action(job, 0);
 if(action<0)
   action= 0;

 dir_node= (IsoDir *) dir_node_generic;
 if(dir_node==NULL) {
   ret= Xorriso_get_volume(xorriso, &volume, 0);
   if(ret<=0)
     {ret= -1; goto ex;}
   ret= Xorriso_make_abs_adr(xorriso, xorriso->wdi, dir_path, path, 1|2|4);
   if(ret<=0)
     goto ex;
   ret= Xorriso_node_from_path(xorriso, volume, path, &iso_node, 0);
   dir_node= (IsoDir *) iso_node;
   if(ret<=0)
     {ret= 0; goto ex;}
   ret= Xorriso_fake_stbuf(xorriso, "", dir_stbuf, &iso_node, 1);
   dir_node= (IsoDir *) iso_node;
   if(ret<=0)
     goto ex;

   name= strrchr(dir_path, '/');
   if(name==NULL)
     name= dir_path;
   else
     name++;
   ret= Findjob_test(job, name, NULL, dir_stbuf, depth, 0);
   if(ret<0)
     goto ex;
   if(ret>0) {
     ret= Xorriso_findi_action(xorriso, job,
                               (IsoDirIter *) boss_iter, boss_mem,
                               path, dir_path, (IsoNode *) dir_node, depth,
                               flag&(1|2));
     if(ret<=0)
       goto ex;
     if(ret==2) {
       deleted= 1;
       goto ex;
     }
     if(ret==3)
       no_dive= 1;
   }
 }
 if(no_dive || !LIBISO_ISDIR((IsoNode *) dir_node))
   {ret= 1; goto ex;}

 mem= boss_mem;
 hflag= 1;
 if(action==1 || action==2 || action==3 || action==14)
   hflag|= 2; /* need freedom to manipulate image */
 if(action==14 || action==17)
   hflag|= 4; /* need LBA sorted iteration for good data reading performance */
 ret= Xorriso_findi_iter(xorriso, dir_node, &mem,
                         &iter, &node_array, &node_count, &node_idx,
                         &node, hflag);
 if(ret<=0)
   goto ex;
 while(1) {
   ret= Xorriso_findi_iter(xorriso, dir_node, &mem, &iter,
                           &node_array, &node_count, &node_idx, &node, 0);
   if(ret<0)
     goto ex;
   if(ret==0 || xorriso->request_to_abort)
 break;
   name= (char *) iso_node_get_name(node);
   ret= Xorriso_make_abs_adr(xorriso, dir_path, name, path, 4);
   if(ret<=0)
     goto ex;
   ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1);
   if(ret<0)
     goto ex;
   if(ret==0)
 continue;

/* ??? This seems to be redundant with the single test above
   ??? Should i dive in unconditionally and leave out test and action here ?
   ??? Then do above test unconditionally ?
   --- Seems that the current configuration represents the special
       handling of the find start path with mount points. Dangerous to change.
*/
   ret= Findjob_test(job, name, dir_stbuf, &stbuf, depth, 0);
   if(ret<0)
     goto ex;
   if(ret>0) {
     ret= Xorriso_make_abs_adr(xorriso, xorriso->wdi, path, abs_path, 1|2|4);
     if(ret<=0)
       goto ex;
     ret= Xorriso_findi_action(xorriso, job, iter, mem,
                               abs_path, path, node, depth, 1|(flag&2));
     if(ret==2) /* node has been deleted */
 continue;
     no_dive= (ret==3);
     if(ret<=0) {
       if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0)
         goto ex;
     }
   }

   if(S_ISDIR(stbuf.st_mode) && !no_dive) {
     ret= Xorriso_findi(xorriso, job, (void *) iter, mem,
                        (void *) node, path, &stbuf, depth+1, flag|1);
     if(ret<0)
       goto ex;
   }
 }

 ret= 1;
ex:;

#ifndef Xorriso_fat_local_meM
 if(path!=NULL)
   free(path);
 if(abs_path!=NULL)
   free(abs_path);
#endif /* ! Xorriso_fat_local_meM */

 Xorriso_process_msg_queues(xorriso,0);

 Xorriso_findi_iter(xorriso, dir_node, &mem, &iter, &node_array, &node_count,
                    &node_idx, &node, (1<<31));
 if(ret<=0)
   return(ret);
 if(deleted)
   return(2);
 return(1);
}


/* @param flag bit0= do not mark image as changed */
int Xorriso_set_volid(struct XorrisO *xorriso, char *volid, int flag)
{
 int ret;
 IsoImage *volume;

 if(xorriso->in_volset_handle == NULL)
   return(2);
 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);
 iso_image_set_volume_id(volume, volid);
 if(!(flag&1))
   xorriso->volset_change_pending= 1;
 Xorriso_process_msg_queues(xorriso,0);
 sprintf(xorriso->info_text,"Volume ID: '%s'",iso_image_get_volume_id(volume));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 return(1);
}


int Xorriso_get_volid(struct XorrisO *xorriso, char volid[33], int flag)
{
 int ret;
 IsoImage *volume;

 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);
 strncpy(volid, iso_image_get_volume_id(volume), 32);
 volid[32]= 0;
 return(1);
}


/* @param flag bit0= do not mark image as changed */
int Xorriso_set_publisher(struct XorrisO *xorriso, char *name, int flag)
{
 int ret;
 IsoImage *volume;

 if(xorriso->in_volset_handle == NULL)
   return(2);
 ret= Xorriso_get_volume(xorriso, &volume, 0);
 if(ret<=0)
   return(ret);
 iso_image_set_publisher_id(volume, name);
 if(!(flag&1))
   xorriso->volset_change_pending= 1;
 Xorriso_process_msg_queues(xorriso,0);
 sprintf(xorriso->info_text,"Publisher: '%s'",
         iso_image_get_publisher_id(volume));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 return(1);
}


/* @param flag bit0=prepare for a burn run */
int Xorriso_set_abort_severity(struct XorrisO *xorriso, int flag)
{
 int ret;

 /* ??? <<< On MISHAP use FAILURE as abort severity known to libisofs.
        On ERRFILE use NEVER.
        The pacifier loop will care for canceling libburn on MISHAP
        and thus also cancel the image generation.
        with libisofs-0.6.4 this should not be necessary
        Shall it be uphold anyway ?
 */
 if((flag&1) && strcmp(xorriso->abort_on_text, "MISHAP")==0)
   ret= iso_set_abort_severity("FAILURE");
 else if((flag&1) && strcmp(xorriso->abort_on_text, "ERRFILE")==0)
   ret= iso_set_abort_severity("NEVER");
 else
   ret= iso_set_abort_severity(xorriso->abort_on_text);
 return(ret>=0);
}


int Xorriso_report_lib_versions(struct XorrisO *xorriso, int flag)
{
 int major, minor, micro;
 int req_major, req_minor, req_micro;

 iso_lib_version(&major, &minor, &micro);
 isoburn_libisofs_req(&req_major, &req_minor, &req_micro);
 sprintf(xorriso->result_line,
         "libisofs   in use :  %d.%d.%d  (min. %d.%d.%d)\n",
         major, minor, micro, req_major, req_minor, req_micro);
 Xorriso_result(xorriso, 0);
 burn_version(&major, &minor, &micro);
 isoburn_libburn_req(&req_major, &req_minor, &req_micro);
 sprintf(xorriso->result_line,
         "libburn    in use :  %d.%d.%d  (min. %d.%d.%d)\n",
         major, minor, micro, req_major, req_minor, req_micro);
 Xorriso_result(xorriso, 0);
 isoburn_version(&major, &minor, &micro);
 sprintf(xorriso->result_line,
         "libisoburn in use :  %d.%d.%d  (min. %d.%d.%d)\n",
         major, minor, micro,
         isoburn_header_version_major, isoburn_header_version_minor,
         isoburn_header_version_micro);
 Xorriso_result(xorriso, 0);
 return(1);
}


/* @param flag bit0= -inq
               bit1= -checkdrive
*/
int Xorriso_atip(struct XorrisO *xorriso, int flag)
{
 int ret, profile_number= 0;
 char *respt, profile_name[80];
 double x_speed_max, x_speed_min= -1.0;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 enum burn_disc_status s;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                           "on attempt to print drive and media info", 2);
 if(ret<=0)
   return(0);
 respt= xorriso->result_line;
 sprintf(respt, "Device type    :");
 ret= burn_drive_get_drive_role(drive);
 if(ret==0)
   sprintf(respt+strlen(respt), "%s\n", "Emulated (null-drive)");
 else if(ret==2)
   sprintf(respt+strlen(respt), "%s\n",
           "Emulated (stdio-drive, 2k random read-write)");
 else if(ret==3)
   sprintf(respt+strlen(respt), "%s\n",
           "Emulated (stdio-drive, sequential write-only)");
 else if(ret!=1)
   sprintf(respt+strlen(respt), "%s\n","Emulated (stdio-drive)");
 else
   sprintf(respt+strlen(respt), "%s\n","Removable CD-ROM");
 sprintf(respt+strlen(respt), "Vendor_info    : '%s'\n",dinfo->vendor);
 sprintf(respt+strlen(respt), "Identifikation : '%s'\n",dinfo->product);
 sprintf(respt+strlen(respt), "Revision       : '%s'\n",dinfo->revision);
 Xorriso_result(xorriso,1);
 if(flag&1)
   return(1);
 sprintf(respt, "Driver flags   : BURNFREE\n");
 sprintf(respt+strlen(respt), "Supported modes: SAO TAO\n");
 Xorriso_result(xorriso,1);
 if(flag&2)
   return(1);

 s= burn_disc_get_status(drive);
 ret= burn_disc_get_profile(drive,&profile_number,profile_name);
 if(ret<=0) {
   profile_number= 0;
   strcpy(profile_name, "-unidentified-");
 }
 if(s != BURN_DISC_UNSUITABLE) {
   ret= burn_disc_read_atip(drive);
   if(ret>0) {
     ret= burn_drive_get_min_write_speed(drive);
     x_speed_min= ((double) ret)/176.4;
   }
 }
 if(s==BURN_DISC_EMPTY) {
   sprintf(respt, "Current: none\n");
   Xorriso_result(xorriso,1);
   return(1);
 } else 
   sprintf(respt, "Current: %s\n",profile_name);
 Xorriso_result(xorriso,1);
 if(strstr(profile_name,"DVD")==profile_name) {
   sprintf(respt, "book type:       %s (emulated booktype)\n", profile_name);
   Xorriso_result(xorriso,1);
   sprintf(respt, "xorriso: message for sdvdbackup: \"(growisofs mode Restricted Overwrite)\"\n");
   Xorriso_result(xorriso,1);
 } else {
   sprintf(respt, "ATIP info from disk:\n");
   Xorriso_result(xorriso,1);
   if(burn_disc_erasable(drive))
     sprintf(respt, "  Is erasable\n");
   else
     sprintf(respt, "  Is not erasable\n");
   Xorriso_result(xorriso,1);
   { int start_lba,end_lba,min,sec,fr;
     ret= burn_drive_get_start_end_lba(drive,&start_lba,&end_lba,0);
     if(ret>0) {
       burn_lba_to_msf(start_lba,&min,&sec,&fr);
       sprintf(respt, "  ATIP start of lead in:  %d (%-2.2d:%-2.2d/%-2.2d)\n",
              start_lba,min,sec,fr);
       Xorriso_result(xorriso,1);
       burn_lba_to_msf(end_lba,&min,&sec,&fr);
       sprintf(respt, "  ATIP start of lead out: %d (%-2.2d:%-2.2d/%-2.2d)\n",
              end_lba,min,sec,fr);
       Xorriso_result(xorriso,1);
     }
   }
   ret= burn_drive_get_write_speed(drive);
   x_speed_max= ((double) ret)/176.4;
   if(x_speed_min<0)
     x_speed_min= x_speed_max;
   sprintf(respt,
          "  1T speed low:  %.f 1T speed high: %.f\n",x_speed_min,x_speed_max);
   Xorriso_result(xorriso,1);
 }
 return(1);
}


int Xorriso_burn_track(struct XorrisO *xorriso, char *track_source, int flag)
{
 int ret, fd, unpredicted_size, profile_number, is_cd= 0;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 struct burn_write_opts *burn_options;
 struct burn_disc *disc= NULL;
 struct burn_session *session;
 struct burn_track *track;
 struct stat stbuf; 
 off_t fixed_size= 0;
 struct burn_source *data_src, *fifo_src;
 enum burn_disc_status disc_state;
 char reasons[BURN_REASONS_LEN], sfe[5*SfileadrL], profile_name[80];


 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to burn track", 2);
 if(ret<=0)
   return(0);
 ret= Xorriso_make_write_options(xorriso, drive, &burn_options, 0);
 if(ret<=0)
   goto ex;

 disc= burn_disc_create();
 session= burn_session_create();
 ret= burn_disc_add_session(disc,session,BURN_POS_END);
 if(ret==0) {
   /* >>> */;
   goto ex;
 }
 track= burn_track_create();
 if(track_source[0] == '-' && track_source[1] == 0) {
   fd= 0;
 } else {
   fd= open(track_source, O_RDONLY);
   if(fd>=0)
     if(fstat(fd,&stbuf)!=-1)
       if((stbuf.st_mode&S_IFMT)==S_IFREG)
         fixed_size= stbuf.st_size;
 }
 if(fixed_size==0)
   unpredicted_size= 1;

 data_src= NULL;
 if(fd>=0)
   data_src= burn_fd_source_new(fd, -1, fixed_size);
 if(data_src==NULL) {
   sprintf(xorriso->info_text, "Could not open data source %s",
           Text_shellsafe(track_source,sfe,0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   ret= 0; goto ex;
 }
 fifo_src= burn_fifo_source_new(data_src, 2048, xorriso->fs, 0);
 if(fifo_src == NULL) {
   sprintf(xorriso->info_text, "Could not create fifo object of 4 MB");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   ret= 0; goto ex;
 }
 xorriso->pacifier_fifo= fifo_src;
 if(burn_track_set_source(track, fifo_src)!=BURN_SOURCE_OK) {
   sprintf(xorriso->info_text,
           "Cannot attach source object to track object");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   ret= 0; goto ex;
 }
 burn_session_add_track(session, track, BURN_POS_END);
 burn_source_free(data_src);

 disc_state = burn_disc_get_status(drive);
 if(disc_state == BURN_DISC_BLANK) {
   /* ok */;
 } else if(disc_state == BURN_DISC_APPENDABLE) {
   if(!isoburn_needs_emulation(drive)) {
     sprintf(xorriso->info_text,
             "Appendable media with data detected. Need blank media.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }
 } else {
   if(disc_state == BURN_DISC_FULL) {
     sprintf(xorriso->info_text,
             "Closed media with data detected. Need blank media.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     if(burn_disc_erasable(drive)) {
       sprintf(xorriso->info_text, "Try --blank_fast\n");
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0);
     }
   } else if(disc_state == BURN_DISC_EMPTY) {
     sprintf(xorriso->info_text, "No media detected in drive");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   } else {
     sprintf(xorriso->info_text,
             "Cannot recognize state of drive and media");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   }
   ret= 0; goto ex;
 }
 if(burn_write_opts_auto_write_type(burn_options, disc, reasons, 0) ==
    BURN_WRITE_NONE) {
   sprintf(xorriso->info_text,
           "Failed to find a suitable write mode with this media.\n");
   sprintf(xorriso->info_text+strlen(xorriso->info_text),
           "Reasons given:\n%s", reasons);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }

 ret= Xorriso_get_profile(xorriso, &profile_number, profile_name, 2);
 is_cd= (ret==2);
 if(isoburn_needs_emulation(drive))
   burn_write_opts_set_start_byte(burn_options, (off_t) 0);
 ret= Xorriso_sanitize_image_size(xorriso, drive, disc, burn_options, 2);
 if(ret<=0) 
   goto ex;

 xorriso->run_state= 1; /* Indicate that burning has started */
 burn_disc_write(burn_options, disc);

 ret= Xorriso_pacifier_loop(xorriso, drive, 1|(is_cd<<4));
 if(ret<=0)
   goto ex;
 if(!burn_drive_wrote_well(drive)) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,
           "libburn indicates failure with writing."); 
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }

 sprintf(xorriso->info_text, "Writing completed sucessfully.\n\n");
 Xorriso_info(xorriso, 0);
 ret= 1;
ex:;
 Xorriso_process_msg_queues(xorriso,0);
 if(disc!=NULL)
   burn_disc_free(disc);
 if(xorriso->pacifier_fifo!=NULL)
   burn_source_free(xorriso->pacifier_fifo);
 xorriso->pacifier_fifo= NULL;
 xorriso->run_state= 0; /* Indicate that burning has ended */
 return(ret);
}



/* @param flag bit1= outdev rather than indev
   @return <0 error, 0 = no profile to see , 1= ok , 2= ok, is CD profile
*/
int Xorriso_get_profile(struct XorrisO *xorriso, int *profile_number, 
                        char profile_name[80], int flag)
{
 int ret;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;

 *profile_number= 0;
 profile_name[0]= 0;
 if(xorriso->out_drive_handle==NULL)
   return(0);
 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to determine media type", flag&2);
 if(ret<=0)
   return(0);
 ret=burn_disc_get_profile(drive, profile_number, profile_name);
 if(ret<=0)
   return(ret);
 if(*profile_number==0x08 || *profile_number==0x09 || *profile_number==0x0a)
   return(2);
 return(0);
}


int Xorriso_iso_file_open(struct XorrisO *xorriso, char *pathname,
                          void **stream, int flag)
{
 int ret;
 char eff_path[SfileadrL];
 IsoNode *node= NULL;
 IsoFile *filenode= NULL;
 IsoStream *iso_stream= NULL;

/* for debugging
#define Libisofs_lba_tesT yes
*/
#ifdef Libisofs_lba_tesT
 uint32_t lba;
#endif

 *stream= NULL;
 ret= Xorriso_get_node_by_path(xorriso, pathname, eff_path, &node, 0);
 if(ret<=0)
   return(ret);
 if(!LIBISO_ISREG(node)) {
   sprintf(xorriso->info_text,
           "Given path does not lead to a regular data file in the image");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }

#ifdef Libisofs_lba_tesT
 ret = iso_node_get_old_image_lba(node, &lba, 0);
 sprintf(xorriso->info_text, "%s : ret= %d , LBA= %lx",
         pathname, ret, (unsigned long) lba);
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
#endif

 filenode= (IsoFile *) node;
 iso_stream= iso_file_get_stream(filenode);
 if(iso_stream==NULL) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,
           "Could not obtain source stream of file in the image for reading");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }
 ret= iso_stream_open(iso_stream);
 if(ret<0) {
   sprintf(xorriso->info_text,
           "Could not open data file in the image for reading");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }
 if(!iso_stream_is_repeatable(iso_stream)) {
   sprintf(xorriso->info_text,
           "The data production of the file in the image is one-time only");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   iso_stream_close(iso_stream);
   return(0);
 }
 Xorriso_process_msg_queues(xorriso,0);
 *stream= iso_stream;
 return(1);
}


int Xorriso_iso_file_read(struct XorrisO *xorriso, void *stream, char *buf,
                          int count, int flag)
{
 int ret, rcnt= 0;
 IsoStream *stream_pt;

 stream_pt= (IsoStream *) stream;

 while(rcnt<count) {
   ret= iso_stream_read(stream_pt, (void *) (buf+rcnt), (size_t) (count-rcnt));
   if(ret==0) /* EOF */
 break;
   if(ret<0) { /* error */
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_report_iso_error(xorriso, "", ret, "Error on read",
                              0, "FAILURE", 1 | ((ret == -1)<<2) );
     return(-1);
   }
   rcnt+= ret;
 }
 return(rcnt);
}


int Xorriso_iso_file_close(struct XorrisO *xorriso, void **stream, int flag)
{
 int ret;

 if(*stream==NULL)
   return(0);
 ret= iso_stream_close(*stream);
 if(ret==1)
   *stream= NULL;
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


/* @param flag bit0= in_node is valid, do not resolve iso_adr
*/
int Xorriso_identify_split(struct XorrisO *xorriso, char *iso_adr,
                           void *in_node,
                           struct SplitparT **parts, int *count,
                           struct stat *total_stbuf,  int flag)
{
 int ret, i;
 int partno, total_parts, first_total_parts= -1;
 off_t offset, bytes, total_bytes, first_total_bytes= -1, first_bytes= -1;
 off_t size;
 IsoImage *volume;
 IsoDir *dir_node;
 IsoDirIter *iter= NULL;
 IsoNode *node;
 char *name;
 struct stat stbuf, first_stbuf;

 *count= 0;
 *parts= NULL;

 if(flag&1) {
   node= (IsoNode *) in_node;
 } else {
   ret= Xorriso_get_volume(xorriso, &volume, 0);
   if(ret<=0)
     return(-1);
   ret= Xorriso_node_from_path(xorriso, volume, iso_adr, &node, 1);
   if(ret<=0)
     return(-1);
 }
 if(!LIBISO_ISDIR(node))
   return(0);
 dir_node= (IsoDir *) node;

 ret= iso_dir_get_children(dir_node, &iter);
 if(ret<0) {
cannot_iter:;
   Xorriso_cannot_create_iter(xorriso, ret, 0);
   return(-1);
 }

 for(i= 0; iso_dir_iter_next(iter, &node) == 1; i++) {
   name= (char *) iso_node_get_name(node);
   ret= Splitpart__parse(name, &partno, &total_parts,
                         &offset, &bytes, &total_bytes, 0);
   if(ret<=0)
     {ret= 0; goto ex;}
   if(i==0) {
     first_total_parts= total_parts;
     first_bytes= bytes;
     first_total_bytes= total_bytes;
     Xorriso_fake_stbuf(xorriso, "", &first_stbuf, &node, 1);
     size= first_stbuf.st_size;
   } else {
     if(first_total_parts!=total_parts || first_total_bytes!=total_bytes ||
        (first_bytes!=bytes && partno!=total_parts))
       {ret= 0; goto ex;}
     Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1);
     if(first_stbuf.st_mode != stbuf.st_mode ||
        first_stbuf.st_uid != stbuf.st_uid ||
        first_stbuf.st_gid != stbuf.st_gid ||
        first_stbuf.st_mtime != stbuf.st_mtime ||
        first_stbuf.st_ctime != stbuf.st_ctime)
       {ret= 0; goto ex;} 
     size= stbuf.st_size;
   }
   /* check for plausible size */
   if(!((partno<total_parts && size==bytes) ||
        (partno==total_parts && size<=bytes)))
     {ret= 0; goto ex;} 
   (*count)++;
 }
 if((*count)<=0)
   {ret= 0; goto ex;}

 ret= Splitparts_new(parts, (*count)+1, 0); /* (have one end marker item) */
 if(ret<=0)
   return(ret);

 iso_dir_iter_free(iter);
 ret= iso_dir_get_children(dir_node, &iter);
 if(ret<0)
   goto cannot_iter;
 for(i= 0; i<*count; i++) {
   ret= iso_dir_iter_next(iter, &node);
   if(ret!=1)
 break;
   name= (char *) iso_node_get_name(node);
   ret= Splitpart__parse(name, &partno, &total_parts,
                         &offset, &bytes, &total_bytes, 0);
   if(ret<=0)
     {ret= 0; goto ex;}
   ret= Splitparts_set(*parts, i, name, partno, total_parts, offset, bytes,
                       total_bytes, 0);
   if(ret<=0)
     goto ex;
 }

 /* >>> ??? sort list ? */
 for(i= 0; i<*count; i++) {
   /* >>> check for disjointness */;
 }

 memcpy(total_stbuf, &first_stbuf, sizeof(struct stat));
 total_stbuf->st_size= total_bytes;
 ret= 1;
ex:;
 if(iter!=NULL)
   iso_dir_iter_free(iter);
 return(ret);
}


int Xorriso_is_split(struct XorrisO *xorriso, IsoNode *node, int flag)
{
 struct SplitparT *split_parts= NULL;
 int split_count= 0, ret;
 struct stat stbuf;

 ret= Xorriso_identify_split(xorriso, "", (void *) node, &split_parts,
                             &split_count, &stbuf, 1);
 if(split_parts!=NULL)
   Splitparts_destroy(&split_parts, split_count, 0);
 return(ret>0);
}

