

/* 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-2010 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>
#include <utime.h>
#include <sys/wait.h>
#include <pwd.h>
#include <grp.h>

/* for -charset */
#include <iconv.h>
#include <langinfo.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_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 Xorriso__file_start_lba(IsoNode *node, int *lba, int flag);

int Xorriso_read_file_data(struct XorrisO *xorriso, IsoNode *node,
                           char *img_path, char *disk_path,
                           off_t img_offset, off_t disk_offset,
                           off_t bytes, int flag);

int Xorriso_path_from_node(struct XorrisO *xorriso, IsoNode *in_node,
                           char path[SfileadrL], int flag);

int Xorriso_node_from_path(struct XorrisO *xorriso, IsoImage *volume,
                           char *path, IsoNode **node, int flag);

int Xorriso_path_from_lba(struct XorrisO *xorriso, IsoNode *node, int lba,
                          char path[SfileadrL], int flag);

int Xorriso__node_lba_cmp(const void *node1, const void *node2);

int Xorriso_search_hardlinks(struct XorrisO *xorriso, IsoNode *node,
                            int *node_idx, int *min_hl, int *max_hl, 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)))
#define LIBISO_ISBOOT(node) (iso_node_get_type(node) == LIBISO_BOOT)


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


/* Default setting for -compliance */
#define Xorriso_relax_compliance_defaulT \
        (isoburn_igopt_allow_deep_paths | isoburn_igopt_allow_longer_paths | \
         isoburn_igopt_always_gmt | \
         isoburn_igopt_rrip_version_1_10 | isoburn_igopt_aaip_susp_1_10 )

#ifdef NIX
/* <<< */
unsigned long Xorriso_get_di_counteR= 0;
#endif /* NIX */


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


static const char *un0(const char *text)
{
 if(text == NULL)
   return("");
 return(text);
}


int Xorriso_destroy_node_array(struct XorrisO *xorriso, int flag)
{
 int i;

 if(xorriso->node_array != NULL) {
   for(i= 0; i < xorriso->node_counter; i++)
     iso_node_unref((IsoNode *) xorriso->node_array[i]);
   free(xorriso->node_array);
 }
 xorriso->node_array= NULL;
 xorriso->node_counter= xorriso->node_array_size= 0;
 Xorriso_lst_destroy_all(&(xorriso->node_disk_prefixes), 0);
 Xorriso_lst_destroy_all(&(xorriso->node_img_prefixes), 0);
 return(1);
}


/* @param flag bit0= do not destroy hln_array but only hln_targets
*/
int Xorriso_destroy_hln_array(struct XorrisO *xorriso, int flag)
{
 int i;

 
 if(xorriso->hln_array != NULL && !(flag & 1)) {
   for(i= 0; i < xorriso->hln_count; i++)
     iso_node_unref((IsoNode *) xorriso->hln_array[i]);
   free(xorriso->hln_array);
   xorriso->hln_array= NULL;
   xorriso->hln_count= 0;
 }
 if(xorriso->hln_targets != NULL) {
   for(i= 0; i < xorriso->hln_count; i++)
     if(xorriso->hln_targets[i] != NULL)
       free(xorriso->hln_targets[i]);
   free(xorriso->hln_targets);
   xorriso->hln_targets= NULL;
 }
 xorriso->node_targets_availmem= 0;
 return(1);
}


int Xorriso_destroy_di_array(struct XorrisO *xorriso, int flag)
{
 int i;

 if(xorriso->di_array != NULL) {
   for(i= 0; i < xorriso->di_count; i++)
     if(xorriso->di_array[i] != NULL)
       iso_node_unref((IsoNode *) xorriso->di_array[i]);
   free(xorriso->di_array);
   xorriso->di_array= NULL;
 }
 if(xorriso->di_do_widen != NULL) {
   free(xorriso->di_do_widen);
   xorriso->di_do_widen= NULL;
 }
 Xorriso_lst_destroy_all(&(xorriso->di_disk_paths), 0);
 Xorriso_lst_destroy_all(&(xorriso->di_iso_paths), 0);
 xorriso->di_count= 0;

#ifdef NIX
 /* <<< */
 fprintf(stderr, "xorriso_DEBUG: get_di_count= %lu\n",
         Xorriso_get_di_counteR);
#endif /* NIX */

 return(1);
}


int Xorriso_new_node_array(struct XorrisO *xorriso, off_t mem_limit,
                           int addon_nodes, int flag)
{
 int i;

 if(xorriso->node_counter <= 0)
   return(1);

 xorriso->node_array= calloc(xorriso->node_counter + addon_nodes,
                             sizeof(IsoNode *));
 if(xorriso->node_array == NULL) {
   Xorriso_no_malloc_memory(xorriso, NULL, 0);
   return(-1);
 }
 for(i= 0; i < xorriso->node_counter + addon_nodes; i++)
   xorriso->node_array[i]= NULL;
 xorriso->node_array_size= xorriso->node_counter + addon_nodes;
 xorriso->node_counter= 0;
 return(1);
}


/* @param flag bit0= do not allocate hln_array but only hln_targets
*/
int Xorriso_new_hln_array(struct XorrisO *xorriso, off_t mem_limit, int flag)
{
 int i;

 Xorriso_destroy_hln_array(xorriso, flag & 1);
 if(xorriso->hln_count <= 0)
   return(1);

 if(!(flag & 1)) {
   xorriso->hln_array= calloc(xorriso->hln_count, sizeof(char *));
   if(xorriso->hln_array == NULL) {
     Xorriso_no_malloc_memory(xorriso, NULL, 0);
     return(-1);
   }
   for(i= 0; i < xorriso->hln_count; i++)
     xorriso->hln_array[i]= NULL;
 }

 xorriso->hln_targets= calloc(xorriso->hln_count, sizeof(char *));
 if(xorriso->hln_targets == NULL) {
   if(!(flag & 1)) {
     free(xorriso->hln_array);
     xorriso->hln_array= NULL;
   }
   Xorriso_no_malloc_memory(xorriso, NULL, 0);
   return(-1);
 }
 for(i= 0; i < xorriso->hln_count; i++)
   xorriso->hln_targets[i]= NULL;
 xorriso->node_targets_availmem= mem_limit
                                 - xorriso->hln_count * sizeof(void *)
                                 - xorriso->hln_count * sizeof(char *);
 if(xorriso->node_targets_availmem < 0)
   xorriso->node_targets_availmem= 0;
 return(1);
}


int Xorriso__findi_sorted_ino_cmp(const void *p1, const void *p2)
{
 int ret;
 IsoNode *n1, *n2;

 n1= *((IsoNode **) p1);
 n2= *((IsoNode **) p2);

 ret= Xorriso__node_lba_cmp(&n1, &n2);
 if(ret)
   return (ret > 0 ? 1 : -1);
 ret= iso_node_cmp_ino(n1, n2, 0);
 return(ret);
}


/* Not suitable for qsort() but for cross-array comparisons.
   p1 and p2 are actually IsoNode *p1, IsoNode *p2
*/
int Xorriso__hln_cmp(const void *p1, const void *p2)
{
 int ret;

 ret= Xorriso__findi_sorted_ino_cmp(&p1, &p2);
 if(ret)
   return (ret > 0 ? 1 : -1);
 if(p1 != p2)
   return(p1 < p2 ? -1 : 1);
 return(0);
}


/* 
   p1 and p2 are actually IsoNode **p1, IsoNode **p2
*/
int Xorriso__findi_sorted_cmp(const void *p1, const void *p2)
{
 int ret;

 ret= Xorriso__findi_sorted_ino_cmp(p1, p2);
 if(ret)
   return (ret > 0 ? 1 : -1);
 if(p1 != p2)
   return(p1 < p2 ? -1 : 1);
 return(0);
}


int Xorriso_sort_node_array(struct XorrisO *xorriso, int flag)
{
 if(xorriso->node_counter <= 0)
   return(0);
 qsort(xorriso->node_array, xorriso->node_counter, sizeof(IsoNode *),
       Xorriso__findi_sorted_cmp);
 return(1);
}


int Xorriso__search_node(void *node_array[], int n,
                         int (*cmp)(const void *p1, const void *p2),
                         void *node, int *idx, int flag)
{
 int ret, l, r, p, pos;

 if(n == 0)
   return(0);
 l= 0;
 r= n + 1;
 while(1) {
   p= (r - l) / 2;
   if(p == 0)
 break;
   p+= l;

   /* NULL elements may indicate invalid nodes. Their first valid right neigbor
      will serve as proxy. If none exists, then the test pushes leftwards.
    */
   for(pos= p - 1; pos < n; pos++)
     if(node_array[pos] != NULL)
   break;
   if(pos < n)
     ret= (*cmp)(&(node_array[pos]), &node);
   else
     ret= 1;

   if(ret < 0)
     l= p;
   else if(ret > 0)
     r= p;
   else {
     *idx= pos;
     return(1);
   }
 }
 return(0);
}


int Xorriso_search_in_hln_array(struct XorrisO *xorriso,
                                 void *node, int *idx, int flag)
{
 int ret;

 if(xorriso->hln_array == NULL || xorriso->hln_count <= 0)
   return(0);
 ret= Xorriso__search_node(xorriso->hln_array, xorriso->hln_count, 
                           Xorriso__findi_sorted_ino_cmp, node, idx, 0);
 return ret;
}


int Xorriso__get_di(IsoNode *node, dev_t *dev, ino_t *ino, int flag)
{
 int ret, i, i_end, imgid, error_code;
 size_t value_length= 0;
 char *value= NULL, msg[ISO_MSGS_MESSAGE_LEN], severity[80];
 unsigned char *vpt;
 static char *name= "isofs.di";

#ifdef NIX
 /* <<< */
 Xorriso_get_di_counteR++;
#endif /* NIX */

 *dev= 0;
 *ino= 0;
 ret= iso_node_lookup_attr(node, name, &value_length, &value, 0);
 if(ret <= 0) {
   /* Drop any pending messages because there is no xorriso to take them */
   iso_obtain_msgs("NEVER", &error_code, &imgid, msg, severity);
   return(ret);
 }
 vpt= (unsigned char *) value;
 for(i= 1; i <= vpt[0] && i < value_length; i++)
   *dev= ((*dev) << 8) | vpt[i];
 i_end= i + vpt[i] + 1;
 for(i++; i < i_end && i < value_length; i++)
   *ino= ((*ino) << 8) | vpt[i];
 free(value);
 return(1);
}


int Xorriso__di_ino_cmp(const void *p1, const void *p2)
{  
 int ret; 
 IsoNode *n1, *n2;
 dev_t d1, d2;
 ino_t i1, i2;
 
 n1= *((IsoNode **) p1);
 n2= *((IsoNode **) p2);

 ret= Xorriso__get_di(n1, &d1, &i1, 0);
 if(ret <= 0)
   {d1= 0; i1= 0;}
 ret= Xorriso__get_di(n2, &d2, &i2, 0);
 if(ret <= 0)
   {d2= 0; i2= 0;}

 if(d1 < d2)
   return(-1);
 if(d1 > d2)
   return(1);
 if(i1 < i2)
   return(-1);
 if(i1 > i2)
   return(1);
 if(d1 == 0 && i1 == 0 && n1 != n2)
   return(n1 < n2 ? -1 : 1);
 return(0);
} 


int Xorriso__di_cmp(const void *p1, const void *p2)
{  
 int ret; 
 IsoNode *n1, *n2;

 ret= Xorriso__di_ino_cmp(p1, p2);
 if(ret)
   return(ret);
 n1= *((IsoNode **) p1);
 n2= *((IsoNode **) p2);
 if(n1 != n2)
   return(n1 < n2 ? -1 : 1);
 return(0);
} 


int Xorriso__sort_di(void *node_array[], int count, int flag)
{
 if(count <= 0)
   return(0);
 qsort(node_array, count, sizeof(IsoNode *), Xorriso__di_cmp);
 return(1);
}


int Xorriso_invalidate_di_item(struct XorrisO *xorriso, IsoNode *node,
                               int flag)
{
 int ret, idx;

 if(xorriso->di_array == NULL)
   return(1);
 ret= Xorriso__search_node(xorriso->di_array, xorriso->di_count,
                           Xorriso__di_cmp, node, &idx, 0);
 if(ret <= 0)
   return(ret == 0);
 if(xorriso->di_array[idx] != NULL)
   iso_node_unref(xorriso->di_array[idx]);
 xorriso->di_array[idx]= NULL;
 return(1);
}


/* @param flag bit0= return 1 even if matching nodes were found but node is
                     not among them
               bit1= use Xorriso__di_cmp() rather than Xorriso__di_ino_cmp()
*/
int Xorriso_search_di_range(struct XorrisO *xorriso, IsoNode *node,
                            int *idx, int *low, int *high, int flag)
{
 int ret, i, found;
 int (*cmp)(const void *p1, const void *p2)= Xorriso__di_ino_cmp;

 if(flag & 2)
   cmp= Xorriso__di_cmp;

 *high= *low= *idx= -1;                            
 ret= Xorriso__search_node(xorriso->di_array, xorriso->di_count,
                           cmp, node, &found, 0);
 if(ret <= 0)
   return(0);
 *low= *high= found;
 for(i= found + 1; i < xorriso->di_count; i++)
   if(xorriso->di_array[i] != NULL) {
     if((*cmp)(&node, &(xorriso->di_array[i])) != 0)
 break;
     *high= i;
   }
 for(i= found - 1; i >= 0; i--)
   if(xorriso->di_array[i] != NULL) {
     if((*cmp)(&node, &(xorriso->di_array[i])) != 0)
 break;
     *low= i;
   }
 for(i= *low; i <= *high; i++)
   if(xorriso->di_array[i] == node) {
     *idx= i;
 break;
   }
 return(*idx >= 0 || (flag & 1));
}


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


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];
 struct iso_zisofs_ctrl zisofs_ctrl= {0, 6, 15};


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

 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()); */

 isoburn_set_msgs_submit(Xorriso_msgs_submit_void, (void *) xorriso,
                         (3<<2) | 128 , 0);

 sprintf(handler_prefix, "%s : ", xorriso->progname);
 burn_set_signal_handling(handler_prefix, NULL, 0);

 ret = iso_zisofs_get_params(&zisofs_ctrl, 0);
 if (ret == 1) {
   xorriso->zisofs_block_size= xorriso->zisofs_block_size_default=
       (1 << zisofs_ctrl.block_size_log2);
   xorriso->zlib_level= xorriso->zlib_level_default=
       zisofs_ctrl.compression_level;
 }
 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);
 }
 strcpy(xorriso->info_text, "Using ");
 strncat(xorriso->info_text, burn_scsi_transport_id(0), 1024);
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 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;
   Sectorbitmap_destroy(&(xorriso->in_sector_map), 0);
   Xorriso_destroy_di_array(xorriso, 0);
   Xorriso_destroy_hln_array(xorriso, 0);
 }
 if(flag&1) {
   if(xorriso->libs_are_started==0)
     return(0);
   isoburn_finish();
 }
 return(1);
}


/* @param flag
          bit0= if not MMC drive print NOTE and return 2  
          bit1= obtain outdrive, else indrive
          bit4= do not report failure
*/
int Xorriso_get_drive_handles(struct XorrisO *xorriso,
                              struct burn_drive_info **dinfo,
                              struct burn_drive **drive,
                              char *attempt, int flag)
{
 int ret;

 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 && !(flag & 16)) {
   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);
 }
 if(*dinfo==NULL)
   return(0);
 *drive= (*dinfo)[0].drive;
 if(flag & 1) {
   ret= burn_drive_get_drive_role(*drive);
   if(ret != 1) {
     sprintf(xorriso->info_text,
       "Output device is not an MMC drive. Desired operation does not apply.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     return(2);
   }
 }
 return((*drive)!=NULL);
}


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);
  ret= burn_sev_to_text(severity, severity_name, 0);
  if(ret>0)
    return(ret);
 *severity_name= "";
 return(0);
}


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);
   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;
   Sectorbitmap_destroy(&(xorriso->in_sector_map), 0);
   Xorriso_destroy_di_array(xorriso, 0);
   Xorriso_destroy_hln_array(xorriso, 0);
   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, xorriso->in_charset);
 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->in_sector_map= NULL;
 Xorriso_update_volid(xorriso, 0);
 xorriso->volset_change_pending= 0;
 xorriso->no_volset_present= 0;
 return(1);
}


int Xorriso_record_boot_info(struct XorrisO *xorriso, int flag)
{
 int ret;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 IsoImage *image;
 ElToritoBootImage *bootimg;
 IsoFile *bootimg_node;
 IsoBoot *bootcat_node;

 xorriso->loaded_boot_bin_lba= -1;
 xorriso->loaded_boot_cat_path[0]= 0;
 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to record boot LBAs", 0);
 if(ret<=0)
   return(0);
 image= isoburn_get_attached_image(drive);
 if(image == NULL)
   return(0);
 ret= iso_image_get_boot_image(image, &bootimg,
                               &bootimg_node, &bootcat_node);
 iso_image_unref(image); /* release obtained reference */
 if(ret != 1)
   return(0);
 if(bootimg_node != NULL)
   Xorriso__file_start_lba((IsoNode *) bootimg_node,
                           &(xorriso->loaded_boot_bin_lba), 0);
 if(bootcat_node != NULL)
   Xorriso_path_from_lba(xorriso, (IsoNode *) bootcat_node, 0,
                         xorriso->loaded_boot_cat_path, 0);
 return(1);
}


int Xorriso_assert_volid(struct XorrisO *xorriso, int msc1, int flag)
{
 int ret, image_blocks;
 char volid[33];
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;

 if(xorriso->assert_volid[0] == 0)
   return(1);
 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to perform -assert_volid", 0);
 if(ret<=0)
   return(0);
 ret= isoburn_read_iso_head(drive, msc1, &image_blocks, volid, 1);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret <= 0) {
   sprintf(xorriso->info_text,
           "-assert_volid: Cannot determine Volume Id at LBA %d.", msc1);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0,
                       xorriso->assert_volid_sev, 0);
   return(0);
 }
 ret= Sregex_match(xorriso->assert_volid, volid, 0);
 if(ret < 0)
   return(2);
 if(ret == 0) {
   strcpy(xorriso->info_text,
          "-assert_volid: Volume id does not match pattern: ");
   Text_shellsafe(xorriso->assert_volid, xorriso->info_text, 1);
   strcat(xorriso->info_text, " <> ");
   Text_shellsafe(volid, xorriso->info_text, 1);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0,
                       xorriso->assert_volid_sev, 0);
   return(0);
 }
 return(ret);
}


/* @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
               bit5= do not print toc
               bit6= do not calm down drive after aquiring it
   @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, aquire_flag, load_lba, ext;
 int lba, track, session, params_flag, adr_mode, read_ret;
 uint32_t size;
 struct burn_drive_info *dinfo= NULL, *out_dinfo, *in_dinfo;
 struct burn_drive *drive= NULL, *out_drive, *in_drive;
 enum burn_disc_status state;
 IsoImage *volset = NULL;
 IsoNode *root_node;
 struct isoburn_read_opts *ropts= NULL;
 char libburn_adr[SfileadrL], *boot_fate, *sev;
 size_t value_length;
 char *value= NULL;
 double num;
 char volid[33], adr_data[163], *adr_pt;

 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);
 if(flag & 1) 
   xorriso->isofs_st_out= time(0) - 1;

 ret= Xorriso_auto_driveadr(xorriso, adr, libburn_adr, 0);
 if(ret <= 0)
   return(ret);
 if(strcmp(libburn_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(libburn_adr, "stdio:/dev/fd/%d", xorriso->dev_fd_1);
   }
 }

 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;
     xorriso->indev_is_exclusive= xorriso->outdev_is_exclusive;
   }
 } 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 outdev with indev", 0);
   if(ret<=0)
     goto ex;
   ret= burn_drive_equals_adr(in_drive, libburn_adr, 1);
   if(ret==1) {
     dinfo= in_dinfo;
     xorriso->outdev_is_exclusive= xorriso->indev_is_exclusive;
   }
 }

 if(dinfo==NULL) {
   aquire_flag= 1 | ((flag&(8|4))>>1) | ((xorriso->toc_emulation_flag & 3)<<3);
   if(xorriso->toc_emulation_flag & 4)
     aquire_flag|= 128;
   if(!(xorriso->do_aaip & 1))
     aquire_flag|= 32;
   if((xorriso->ino_behavior & (1 | 2)) && !(xorriso->do_aaip & (4 | 32)))
     aquire_flag|= 64;
   burn_preset_device_open(xorriso->drives_exclusive, 0, 0);
   ret= isoburn_drive_aquire(&dinfo, libburn_adr, aquire_flag);
   burn_preset_device_open(1, 0, 0);
   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;
   }
   if(flag&1)
     if(xorriso->image_start_mode&(1<<31)) /* used up setting */
       xorriso->image_start_mode= 0; /* no need to perform auto setting */
   if(flag & 1)
     xorriso->indev_is_exclusive= xorriso->drives_exclusive;
   if(flag & 2)
     xorriso->outdev_is_exclusive= xorriso->drives_exclusive;
 }
 drive= dinfo[0].drive;
 if(flag&1) {
   if(xorriso->image_start_mode&(1<<31)) /* used up setting */
     xorriso->image_start_mode&= ~0xffff; /* perform auto setting */
   if((xorriso->image_start_mode&(1<<30))) { /* if enabled at all */
     adr_pt= xorriso->image_start_value;
     adr_mode= xorriso->image_start_mode & 0xffff;
     if(adr_mode == 4 && strlen(adr_pt) <= 80) {
       /* Convert volid search expression into lba */
       params_flag= 0;
       ret= Xorriso__bourne_to_reg(xorriso->image_start_value, adr_data, 0);
       if(ret == 1)
         params_flag|= 4;
       ret= isoburn_get_mount_params(drive, 4, adr_data, &lba, &track,
                                     &session, volid, params_flag);
       Xorriso_process_msg_queues(xorriso,0);
       if(ret <= 0)
         goto ex;
       if(session <= 0 || track <= 0 || ret == 2) {
         Xorriso_msgs_submit(xorriso, 0,
                "-load : Given address does not point to an ISO 9660 session",
                0, "FAILURE", 0);
         ret= 0; goto ex;
       }
       sprintf(volid, "%d", lba);
       adr_pt= volid;
       adr_mode= 3;
     }
     ret= isoburn_set_msc1(drive, adr_mode, adr_pt,
                           !!(xorriso->image_start_mode & (1<<16)));
     if(ret<=0)
       goto ex;
     if(xorriso->image_start_mode&(1<<31))
       xorriso->image_start_mode= 0; /* disable msc1 setting completely */
     else
       xorriso->image_start_mode|= (1<<31); /* mark as used up */
   }
 }
 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, "NOTE", 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;}
     }
   }
   if(!(flag&32))
     Xorriso_toc(xorriso, 1 | 2 | 8);
   {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;
 Sectorbitmap_destroy(&(xorriso->in_sector_map), 0);
 Xorriso_destroy_hln_array(xorriso, 0);
 Xorriso_destroy_di_array(xorriso, 0);

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

 ext= isoburn_ropt_noiso1999;
 if((xorriso->ino_behavior & (1 | 2)) && !(xorriso->do_aaip & (1 | 4 | 32))
    && !(xorriso->do_md5 & 1)) 
   ext|= isoburn_ropt_noaaip; 
 if(!(xorriso->do_aaip & 1)) 
   ext|= isoburn_ropt_noacl; 
 if(!(xorriso->do_aaip & 4))
   ext|= isoburn_ropt_noea; 
 if(xorriso->ino_behavior & 1)
   ext|= isoburn_ropt_noino;

#ifdef isoburn_ropt_nomd5
 if(!(xorriso->do_md5 & 1))
   ext|= isoburn_ropt_nomd5;
#endif

 isoburn_ropt_set_extensions(ropts, ext);

 isoburn_ropt_set_default_perms(ropts, (uid_t) 0, (gid_t) 0, (mode_t) 0555);
 isoburn_ropt_set_input_charset(ropts, xorriso->in_charset);
 isoburn_ropt_set_auto_incharset(ropts, !!(xorriso->do_aaip & 512));
 
 Xorriso_set_image_severities(xorriso, 1); /* No DEBUG messages */
 Xorriso_pacifier_reset(xorriso, 0);
 isoburn_set_read_pacifier(drive, Xorriso__read_pacifier, (void *) xorriso);

 /* <<< Trying to work around too much tolerance on bad image trees.
        Better would be a chance to instruct libisofs what to do in
        case of image read errors. There is a risk to mistake other SORRYs.
 */
 if(xorriso->img_read_error_mode>0)
   iso_set_abort_severity("SORRY");

 if(state != BURN_DISC_BLANK) {
   ret= isoburn_disc_get_msc1(drive, &load_lba);
   if(ret > 0) {
     sprintf(xorriso->info_text,
             "Loading ISO image tree from LBA %d", load_lba);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0); 
   }
   ret= Xorriso_assert_volid(xorriso, load_lba, 0);
   if(ret <= 0)
     goto ex;
 }

 read_ret= ret= isoburn_read_image(drive, ropts, &volset);

 /* <<< Resetting to normal thresholds */
 if(xorriso->img_read_error_mode>0)
   Xorriso_set_abort_severity(xorriso, 0);

 if(ret<=0) {
   Xorriso_process_msg_queues(xorriso,0);
   Xorriso_set_image_severities(xorriso, 0);
   Xorriso_give_up_drive(xorriso, 1|((flag&32)>>2));
   sprintf(xorriso->info_text,"Cannot read ISO image tree");
   sev= "FAILURE";
   if(xorriso->img_read_error_mode==2)
     sev= "FATAL";
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, sev, 0);
   if(read_ret == ISO_SB_TREE_CORRUPTED && (xorriso->do_md5 & 1)) {
     Xorriso_msgs_submit(xorriso, 0,
           "You might get a questionable ISO image tree by option -md5 'off'.",
            0, "HINT", 0);
   } else if(xorriso->img_read_error_mode!=0) {
     Xorriso_msgs_submit(xorriso, 0, "You might get a partial or altered ISO image tree by option -error_behavior 'image_loading' 'best_effort' if -abort_on is set to be tolerant enough.",
                         0, "HINT", 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->in_sector_map= NULL;
 Xorriso_set_image_severities(xorriso, 0);

 Xorriso_update_volid(xorriso, 0);
 strncpy(xorriso->application_id,
         un0(iso_image_get_application_id(volset)), 128);
 xorriso->application_id[128]= 0;
 strncpy(xorriso->publisher, un0(iso_image_get_publisher_id(volset)), 128);
 xorriso->publisher[128]= 0;
 strncpy(xorriso->system_id, un0(iso_image_get_system_id(volset)), 32);
 xorriso->system_id[32]= 0;
 strncpy(xorriso->volset_id, un0(iso_image_get_volset_id(volset)), 128);
 xorriso->volset_id[128]= 0;

 /* <<< can be removed as soon as libisofs-0.6.24 is mandatory
 */
 if(strcmp(un0(iso_image_get_copyright_file_id(volset)), "_") == 0 &&
    strcmp(un0(iso_image_get_abstract_file_id(volset)), "_") == 0 &&
    strcmp(un0(iso_image_get_biblio_file_id(volset)), "_") == 0) {
   /* This is bug output from libisofs <= 0.6.23 . The texts mean file names
      and should have been empty to indicate that there are no such files.
      It is obvious that not all three roles can be fulfilled by one file "_"
      so that one cannot spoil anything by assuming them empty now.
      Modern versions of libisofs are supposed to do this themselves.
    */
   iso_image_set_copyright_file_id(volset, "");
   iso_image_set_abstract_file_id(volset, "");
   iso_image_set_biblio_file_id(volset, "");
 }

 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->boot_image_bin_path[0])
     boot_fate= "replaced by an isolinux image";
   else 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_record_boot_info(xorriso, 0);
 }

 if(flag & 1) {
   /* Look for isofs.st and put it into xorriso->isofs_st_in */;
   root_node= (IsoNode *) iso_image_get_root(volset);
   ret= iso_node_lookup_attr(root_node, "isofs.st", &value_length, &value, 0);
   if(ret > 0) {
     if(value_length > 0) {
       sscanf(value, "%lf", &num);
       if(num > 0)
         xorriso->isofs_st_in= num;
     }
     free(value);
   }
 }

 if(!(flag&32)) {
   Xorriso_toc(xorriso, 1 | 8);
   if(xorriso->loaded_volid[0]!=0) {
     sprintf(xorriso->info_text,"Volume id    : '%s'\n",
             xorriso->loaded_volid);
     Xorriso_info(xorriso, 0);
     if(strcmp(xorriso->loaded_volid, xorriso->volid) != 0 &&
        !xorriso->volid_default) {
       sprintf(xorriso->info_text, "New volume id: '%s'\n", xorriso->volid);
       Xorriso_info(xorriso, 0);
     }
   }
 }

 ret= 1+not_writeable;
ex:
 Xorriso_process_msg_queues(xorriso,0);
 if(ret<=0) {
   hret= Xorriso_give_up_drive(xorriso, (flag&3)|((flag&32)>>2));
   if(hret<ret)
     ret= hret;
 } else {
   if(drive != NULL && (xorriso->do_calm_drive & 1) && !(flag & 64))
     burn_drive_snooze(drive, 0); /* No need to make noise from start */
 }
 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, do_eject;
 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) {
     do_eject= !!(flag&4);
     if((flag & 4) && !xorriso->indev_is_exclusive) {
       sprintf(xorriso->info_text,
               "Will not eject media in non-exclusively aquired input drive.");
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0);
       do_eject= 0;
     }
     if(drive!=NULL)
       isoburn_drive_release(drive, do_eject);
     if(dinfo!=NULL)
       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;
   Sectorbitmap_destroy(&(xorriso->in_sector_map), 0);
   Xorriso_destroy_di_array(xorriso, 0);
   Xorriso_destroy_hln_array(xorriso, 0);
   xorriso->loaded_volid[0]= 0;
   xorriso->isofs_st_out= time(0) - 1;
   xorriso->isofs_st_in= 0;
   xorriso->volset_change_pending= 0;
   xorriso->no_volset_present= 0; 
   xorriso->loaded_boot_bin_lba= 0;
   xorriso->loaded_boot_cat_path[0]= 0;
   in_is_out_too= 0;
 }
 if((flag&2) && xorriso->out_drive_handle!=NULL) {
   do_eject= !!(flag&4);
   if((flag & 4) && !xorriso->outdev_is_exclusive) {
     sprintf(xorriso->info_text,
             "Will not eject media in non-exclusively aquired drive.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0);
     do_eject= 0;
   }
   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, do_eject);
     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 | 8);
   }
 }
 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, stream_mode= 0;

 *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);
 if(xorriso->do_stream_recording == 1)
   stream_mode= 1;
 else if(xorriso->do_stream_recording == 2)
   stream_mode= 51200; /* 100 MB */
 else if(xorriso->do_stream_recording >= 16)
   stream_mode= xorriso->do_stream_recording;
 burn_write_opts_set_stream_recording(*burn_options, stream_mode);

#ifdef Xorriso_dvd_obs_default_64K
 if(xorriso->dvd_obs == 0)
   burn_write_opts_set_dvd_obs(*burn_options, 64 * 1024);
 else  
#endif
   burn_write_opts_set_dvd_obs(*burn_options, xorriso->dvd_obs);

 burn_write_opts_set_stdio_fsync(*burn_options, xorriso->stdio_sync);
 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, lba, nwa;
 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 && ret>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);
   }
 }
 if(xorriso->alignment == 0) {
   ret= isoburn_needs_emulation(drive);
   if(ret > 0) {
     /* Take care that the session is padded up to the future NWA.
        Else with padding < 32 it could happen that PVDs from older
        sessions survive and confuse -rom_toc_scan.
      */
     xorriso->alignment= 32;
   }
 }
 if(xorriso->alignment > 0) {
   if(img_sectors > 0) {
     ret= isoburn_disc_track_lba_nwa(drive, burn_options, 0, &lba, &nwa);
     if(ret <= 0)
       nwa= 0;
     lba= (nwa + img_sectors + padding) % xorriso->alignment;
     if(lba > 0)
       padding+= xorriso->alignment - lba;
   } else if(padding < xorriso->alignment)
     padding= xorriso->alignment;
 }
 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);
}


/* @return <0 yes , 0 no , <0 error */
int Xorriso_is_isohybrid(struct XorrisO *xorriso, IsoFile *bootimg_node,
                         int flag)
{
 int ret;
 unsigned char buf[68];
 void *data_stream= NULL;

 ret= Xorriso_iso_file_open(xorriso, "", (void *) bootimg_node,
                            &data_stream, 1);
 if(ret <= 0)
   return(-1);
 ret= Xorriso_iso_file_read(xorriso, data_stream, (char *) buf, 68, 0);
 Xorriso_iso_file_close(xorriso, &data_stream, 0);
 if(ret <= 0)
   return(0);
 if(buf[64] == 0xfb && buf[65] == 0xc0 && buf[66] == 0x78 && buf[67] == 0x70)
   return(1);
 return(0);
}


int Xorriso_set_isolinux_options(struct XorrisO *xorriso,
                                 IsoImage *image, int flag)
{
 int make_isohybrid_mbr= 0, ret;
 ElToritoBootImage *bootimg;
 IsoFile *bootimg_node;

 ret= iso_image_get_boot_image(image, &bootimg, &bootimg_node, NULL);
 if(ret != 1) {
   sprintf(xorriso->info_text, "Programming error: No boot image available in Xorriso_set_isolinux_options()");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }

 if(xorriso->boot_image_isohybrid == 0) {
   ret= el_torito_set_isolinux_options(bootimg, 1, 0);
   return(ret == 1);
 }
 if(xorriso->boot_image_isohybrid == 3) {
   make_isohybrid_mbr= 1;
 } else {
   ret= Xorriso_is_isohybrid(xorriso, bootimg_node, 0);
   if(ret < 0)
     return(0);
   if(ret > 0)
     make_isohybrid_mbr= 1;
 }

 if(xorriso->boot_image_isohybrid == 2 && !make_isohybrid_mbr) {
   sprintf(xorriso->info_text,
          "Isohybrid signature is demanded but not found in boot image file.");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }
 if(make_isohybrid_mbr) {
   sprintf(xorriso->info_text, "Will write isohybrid MBR.");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   xorriso->alignment= 512;
 }

 ret= el_torito_set_isolinux_options(bootimg, 1 | (make_isohybrid_mbr << 1),0);
 return(ret == 1);
}


int Xorriso_auto_format(struct XorrisO *xorriso, int flag)
{
 int ret, profile, status, num_formats;
 char profile_name[80];
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 off_t size;
 unsigned dummy;

 ret= Xorriso_may_burn(xorriso, 0);
 if(ret <= 0)
   return(0);

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to autoformat", 2);
 if(ret<=0)
   return(0);
 ret= burn_disc_get_profile(drive, &profile, profile_name);
 if(ret>0 && (profile==0x12 || profile==0x43)) { /* DVD-RAM or BD-RE */
   ret= burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats);
   if(ret>0 && status==BURN_FORMAT_IS_UNFORMATTED) {
     sprintf(xorriso->info_text,
             "Unformatted %s media detected. Trying -format fast.",
             profile_name);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     ret= Xorriso_format_media(xorriso, (off_t) 0, 1 | 4);
     if(ret<=0) {
       sprintf(xorriso->info_text, "Automatic formatting of %s failed",
               profile_name);
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       return(ret);
     }
   }
 }
 return(1);
}


#define Xorriso_with_make_isohybrid_mbR 1


/* @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, pacifier_speed= 0, data_lba, ext;
 int major, minor, micro;
 char xorriso_id[256], *img_id, sfe[5*SfileadrL], *cpt, *out_cs;
 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;
 IsoNode *node, *root_node;
 ElToritoBootImage *bootimg;
 enum eltorito_boot_media_type emul_type= ELTORITO_NO_EMUL;
 int profile_number;
 char profile_name[80];

 ret= Xorriso_finish_hl_update(xorriso, 0);
 if(ret <= 0)
   return(ret);

 out_cs= xorriso->out_charset;
 if(out_cs == NULL)
   Xorriso_get_local_charset(xorriso, &out_cs, 0);

 if(!(flag & 1)) {
   ret= Xorriso_auto_format(xorriso, 0);
   if(ret <=0 )
     return(0);
 }
 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 non-zero data");
     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);
 if(ret == 2)
   pacifier_speed= 1;
 else if(ret == 3)
   pacifier_speed= 2;

 ret= isoburn_igopt_new(&sopts, 0);
 if(ret<=0) {
   Xorriso_process_msg_queues(xorriso, 0);
   return(ret);
 }
 relax= xorriso->relax_compliance;

 xorriso->alignment= 0;
 image= isoburn_get_attached_image(source_drive);
 if(image != NULL) {
   iso_image_set_application_id(image, xorriso->application_id);
   iso_image_set_publisher_id(image, xorriso->publisher);
   iso_image_set_system_id(image, xorriso->system_id);
   iso_image_set_volset_id(image, xorriso->volset_id);
 }

 if((xorriso->do_aaip & 256) && out_cs != NULL) {
   static char *names = "isofs.cs";
   size_t value_lengths[1];

   value_lengths[0]= strlen(out_cs);
   ret= Xorriso_setfattr(xorriso, NULL, "/",
                         (size_t) 1, &names, value_lengths, &out_cs, 2 | 8);
   if(ret<=0)
     goto ex;
 }
 
 /* Activate, adjust or discard boot image */
 /* >>> ??? move down to libisoburn ? */
 if(image!=NULL && !(flag&1)) {
   ret= iso_image_get_boot_image(image, &bootimg, NULL, NULL);
   
   if(xorriso->boot_image_bin_path[0]) {
     /*  discard old boot image, set new one */
     if(ret == 1)
       iso_image_remove_boot_image(image);
     if(xorriso->boot_image_emul == 1)
       emul_type= ELTORITO_HARD_DISC_EMUL;
     else if(xorriso->boot_image_emul == 2)
       emul_type= ELTORITO_FLOPPY_EMUL;
     if(xorriso->boot_image_cat_path[0] == 0) {
       strcpy(xorriso->boot_image_cat_path, xorriso->boot_image_bin_path);
       cpt= strrchr(xorriso->boot_image_cat_path, '/');
       if(cpt == NULL)
         cpt= xorriso->boot_image_cat_path;
       else
         cpt++;
       strcpy(cpt, "boot.cat");
     }
     sprintf(xorriso->info_text, "Activating alleged isolinux boot image %s",
             Text_shellsafe(xorriso->boot_image_bin_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     sprintf(xorriso->info_text, "Creating El Torito boot catalog file %s",
             Text_shellsafe(xorriso->boot_image_cat_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);

     ret= Xorriso_node_from_path(xorriso, image, xorriso->boot_image_bin_path,
                                 &node, 1);
     if(ret <= 0) {
       sprintf(xorriso->info_text,
               "Cannot find in ISO image: -boot_image ... bin_path=%s",
               Text_shellsafe(xorriso->boot_image_bin_path, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       {ret= 0; goto ex;}
     }
     ret= Xorriso_node_from_path(xorriso, image, xorriso->boot_image_cat_path,
                                 &node, 1);
     if(ret > 0) {
       if(!xorriso->do_overwrite) {
         sprintf(xorriso->info_text,
                 "May not overwite existing -boot_image ... cat_path=%s",
                 Text_shellsafe(xorriso->boot_image_cat_path, sfe, 0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         {ret= 0; goto ex;}
       }
       ret= Xorriso_rmi(xorriso, NULL, (off_t) 0, xorriso->boot_image_cat_path,
                        8 | (xorriso->do_overwrite == 1));
       if(ret != 1) {
         sprintf(xorriso->info_text,
                 "Could not remove existing -boot_image cat_path=%s",
                 Text_shellsafe(xorriso->boot_image_cat_path, sfe, 0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         {ret= 0; goto ex;}
       }
     }

     ret= iso_image_set_boot_image(image, xorriso->boot_image_bin_path,
                                   emul_type, xorriso->boot_image_cat_path,
                                   &bootimg);
     if(ret < 0) {
       Xorriso_process_msg_queues(xorriso,0);
       Xorriso_report_iso_error(xorriso, "", ret,
                 "Error when attaching El-Torito boot image to ISO 9660 image",
                 0, "FAILURE", 1);
       sprintf(xorriso->info_text,
               "Could not attach El-Torito boot image to ISO 9660 image");
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       {ret= 0; goto ex;}
     }
     el_torito_set_load_size(bootimg, xorriso->boot_image_load_size / 512); 

#ifdef Xorriso_with_make_isohybrid_mbR

     ret= Xorriso_set_isolinux_options(xorriso, image, 0);
     if(ret <= 0)
       goto ex;
    
#else
     el_torito_patch_isolinux_image(bootimg);
#endif

   } else 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);

       ret= Xorriso_path_from_lba(xorriso, NULL, xorriso->loaded_boot_bin_lba,
                                   sfe, 1);
       if(ret < 0)
         goto ex;
       if(ret == 0) {
         sprintf(xorriso->info_text,
                 "Cannot patch boot image: no file found for its LBA.");
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         sprintf(xorriso->info_text,
           "Probably the loaded boot image file was deleted in this session.");
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
         sprintf(xorriso->info_text,
           "Use -boot_image \"any\" \"discard\" or set new boot image");
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0);
         goto ex;
       }
    
#ifdef Xorriso_with_make_isohybrid_mbR

       ret= Xorriso_set_isolinux_options(xorriso, image, 0);
       if(ret <= 0)
         goto ex;

#else
       el_torito_patch_isolinux_image(bootimg);
#endif

     } 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);
   }
 }

 if((xorriso->do_aaip & 16) || !(xorriso->ino_behavior & 2)) {
   /* Overwrite isofs.st of root node by xorriso->isofs_st_out */
   char *name= "isofs.st";
   char timestamp[16], *value= timestamp;
   size_t value_length;

   sprintf(timestamp, "%.f", (double) xorriso->isofs_st_out);
   value_length= strlen(timestamp);
   Xorriso_setfattr(xorriso, NULL, "/", (size_t) 1, &name,
                    &value_length, &value, 2 | 8);
 }

 isoburn_igopt_set_level(sopts, 3);
 ext= isoburn_igopt_rockridge |
      ((!!xorriso->do_joliet) * isoburn_igopt_joliet) |
      (( !(xorriso->ino_behavior & 2)) * isoburn_igopt_hardlinks) |
      (( (!(xorriso->ino_behavior & 2)) ||
         (xorriso->do_aaip & (2 | 8 | 16 | 256)) ||
         (xorriso->do_md5 & (2 | 4))
       ) * isoburn_igopt_aaip) |
      ((!!(xorriso->do_md5 & 2)) * isoburn_igopt_session_md5) |
      ((!!(xorriso->do_md5 & 4)) * isoburn_igopt_file_md5) |
      ((!!(xorriso->do_md5 & 8)) * isoburn_igopt_file_stability);
 isoburn_igopt_set_extensions(sopts, ext);
 isoburn_igopt_set_relaxed(sopts, relax);
 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, out_cs);
 isoburn_igopt_set_fifo_size(sopts, xorriso->fs * 2048);
 Ftimetxt(time(NULL), xorriso->scdbackup_tag_time, 8);
 isoburn_igopt_set_scdbackup_tag(sopts, xorriso->scdbackup_tag_name,
                                 xorriso->scdbackup_tag_time,
                                 xorriso->scdbackup_tag_written);
 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;}

 if(xorriso->zisofs_by_magic) {
   sprintf(xorriso->info_text,
           "Checking disk file content for zisofs compression headers.");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0);
   root_node= (IsoNode *) iso_image_get_root(image);
   ret= iso_node_zf_by_magic(root_node,
             (xorriso->out_drive_handle == xorriso->in_drive_handle) | 2 | 16);
   if(ret<0) {
     Xorriso_report_iso_error(xorriso, "", ret,
              "Error when examining file content for zisofs headers",
              0, "FAILURE", 1);
   }
   ret= Xorriso_eval_problem_status(xorriso, 1, 0);
   if(ret<0)
     {ret= 0; goto ex;}
   sprintf(xorriso->info_text,
           "Check for zisofs compression headers done.");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0);
 }

 /* >>> omit iso_image_update_sizes if the image was filled up very quickly */;

 ret= iso_image_update_sizes(image);
 if(ret < 0) {
   Xorriso_process_msg_queues(xorriso, 0);
   if(ret<0) {
     Xorriso_report_iso_error(xorriso, "", ret,
              "Error when updating file sizes",
              0, "FAILURE", 1);
   }
   ret= Xorriso_eval_problem_status(xorriso, 1, 0);
   if(ret<0)
     {ret= 0; goto ex;}
 }

 Xorriso_set_abort_severity(xorriso, 1);
 if (xorriso->grow_blindly_msc2 >= 0 &&
     xorriso->out_drive_handle != xorriso->in_drive_handle) {
   ret= isoburn_prepare_blind_grow(source_drive, &disc, sopts, drive,
                                   xorriso->grow_blindly_msc2);
   if(ret>0) {
     /* Allow the consumer of output to access the input drive */
     source_drive= NULL;
     ret= Xorriso_give_up_drive(xorriso, 1|8); 
     if(ret<=0)
       goto ex;
   }
 } else 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;}
 }

 ret= Xorriso_make_write_options(xorriso, drive, &burn_options, 0);
 if(ret<=0)
   goto ex;
 isoburn_igopt_get_effective_lba(sopts, &(xorriso->session_lba));
 if(xorriso->do_stream_recording == 2) {
   ret= isoburn_igopt_get_data_start(sopts, &data_lba);
   if(ret > 0 && data_lba >= 16)
     burn_write_opts_set_stream_recording(burn_options, data_lba);
 }

 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;
 }

 ret= Xorriso_may_burn(xorriso, 0);
 if(ret <= 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, pacifier_speed << 4);
 if(ret<=0)
   goto ex;
 if(!isoburn_drive_wrote_well(drive)) {
   isoburn_cancel_prepared_write(source_drive, drive, 0);
   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\nWritten 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 to %s completed sucessfully.\n\n",
         Text_shellsafe(xorriso->outdev,sfe,0));
 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);
 if(image != NULL)
   iso_image_unref(image);
 isoburn_igopt_destroy(&sopts, 0);
 Xorriso_process_msg_queues(xorriso,0);
 Xorriso_append_scdbackup_record(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= mkisofs
                        2= cdrecord
               bit4= report speed in CD units
               bit5= report speed in BD 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, date_text[80], *speed_unit, mem_text[8];
 enum burn_drive_status drive_status;
 double start_time, current_time, last_time, base_time= 0.0, base_count= 0.0;
 double next_base_time= 0.0, next_base_count= 0.0, first_base_time= 0.0;
 double first_base_count= 0.0, norm= 0.0, now_time, fract_offset= 0.0;
 double measured_speed, speed_factor= 1385000, quot;
 time_t time_prediction;

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

 emul= flag&15;
 fract_offset= 0.2 * (double) emul - ((int) (0.2 * (double) emul));
 if(emul==0)
   emul= xorriso->pacifier_style;
 speed_unit= "D";
 if(flag&16) {
   speed_factor= 150.0*1024;
   speed_unit= "C";
 } else if(flag & 32) {
   speed_factor= 4495625;
   speed_unit= "B";
 }
 progress.sector= 0;
 current_time= Sfile_microtime(0);
 measured_speed= 0.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(current_time-last_time>0.2)
       measured_speed= (progress.sector - last_sector) * 2048.0 /
                       (current_time - last_time);
     buffer_fill= 50;
     if(progress.buffer_capacity>0)
       buffer_fill= (double) (progress.buffer_capacity
                              - progress.buffer_available) * 100.0
                    / (double) progress.buffer_capacity;
     if(emul==2) {
       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);
       else
         ret= isoburn_get_fifo_status(drive, &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));

       sprintf(xorriso->info_text+strlen(xorriso->info_text), " [buf %3d%%]",
               buffer_fill);

       if(current_time-last_time>0.2)
         sprintf(xorriso->info_text+strlen(xorriso->info_text), "  %4.1fx.",
                 measured_speed/speed_factor);

     } else if(emul == 1 &&
               progress.sectors > 0 && progress.sector <= progress.sectors) {
       /* "37.87% done, estimate finish Tue Jul 15 18:55:07 2008" */

       quot= ((double) progress.sector) / ((double) progress.sectors);
       sprintf(xorriso->info_text, " %2.2f%% done", quot*100.0);
       if(current_time - start_time >= 2 && quot > 0.0 &&
          (quot >= 0.02 || progress.sector >= 5*1024)) {
         if(base_time == 0.0 && progress.sector >= 16*1024) {
           first_base_time= base_time= next_base_time= current_time;
           first_base_count= next_base_count= progress.sector;
         } else if(next_base_time > 0 && current_time - next_base_time >= 10) {
           base_time= next_base_time;
           base_count= next_base_count;
           next_base_time= current_time;
           next_base_count= progress.sector;
         }
         if(first_base_time > 0 &&
            current_time - first_base_time >= 10 &&
            progress.sectors > first_base_count &&
            progress.sector > first_base_count) {
           norm= (1.0 - quot);
           if(norm < 0.0001)
             norm= 0.0001;
           quot= ((double) progress.sector - first_base_count)
                  / ((double) progress.sectors - first_base_count);
           time_prediction= norm * (1.0 - quot) / quot
                            * (current_time - first_base_time);
         } else {
           time_prediction= (1.0 - quot) / quot * (current_time - start_time);
           norm= 1.0;
         }
         if(base_time > 0 && 
            current_time - base_time >= 10 && progress.sectors > base_count) {
           quot= ((double) progress.sector - base_count)
                  / ((double) progress.sectors - base_count);
           time_prediction+= (1.0 - quot) / quot * (current_time - base_time);
           norm+= 1.0;
         }
         time_prediction/= norm;
         if(time_prediction < 30*86400 && time_prediction > 0) {
           time_prediction+= current_time + 1;
           Ftimetxt(time_prediction, date_text, 4);
           sprintf(xorriso->info_text+strlen(xorriso->info_text),
                   ", estimate finish %s", date_text);
         }
       }
     } else {
       if(progress.sector<=progress.sectors) {
         if(progress.sectors <= 0)
           strcpy(mem_text, " 99.9");
         else
           sprintf(mem_text, "%5.1f",
             100.0 * ((double) progress.sector) / ((double) progress.sectors));
         mem_text[5]= 0;
         sprintf(xorriso->info_text, "Writing: %10ds  %s%% ",
                 progress.sector, mem_text);
       } else {
         Sfile_scale(2048.0 * (double) progress.sector, mem_text, 5, 1e4, 1);
         sprintf(xorriso->info_text, "Writing: %10ds   %s ",
                 progress.sector, mem_text);
       }
       ret= isoburn_get_fifo_status(drive, &size, &free_bytes, &status_text);
       if(ret>0 )
         sprintf(xorriso->info_text+strlen(xorriso->info_text),
                 "  fifo %3d%%  buf %3d%%",
                 (int) (100.0-100.0*((double) free_bytes)/(double) size),
                 buffer_fill);
       if(current_time-last_time>0.2)
         sprintf(xorriso->info_text+strlen(xorriso->info_text), "  %5.1fx%s ",
                 measured_speed/speed_factor, speed_unit);
     }
   } 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. Working since %.f seconds.",
             current_time-start_time);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0);

   for(i= 0; i<12; i++) { /* 2 usleeps more than supposed to be needed */
     Xorriso_process_msg_queues(xorriso, 0);
     if(aborting<=0)
       aborting= Xorriso_check_burn_abort(xorriso, 0);
     usleep(100000);
     now_time= Sfile_microtime(0);
     if(((time_t) now_time) - ((time_t) current_time) >= 1 &&
         now_time - ((time_t) now_time) >= fract_offset)
   break;
   }
 }
 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_process_msg_queues(xorriso,0);
 xorriso->pacifier_count++;
 if(xorriso->pacifier_count%10)
   return(1);
 Xorriso_pacifier_callback(xorriso, "nodes read", xorriso->pacifier_count, 0,
                           "", 0);
 return(1);
}

/* @param flag    bit0=silently return 0 if no volume/image is present
*/
int Xorriso_get_volume(struct XorrisO *xorriso, IsoImage **volume,
                       int flag)
{
 if(xorriso->in_volset_handle==NULL) {
   if(flag & 1)
     return(0);
   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= "/";
 if(volume == NULL) {
   ret= Xorriso_get_volume(xorriso, &volume, 0);
   if(ret <= 0)
     return(ret);
 }
 *node= NULL;
 ret= iso_tree_path_to_node(volume, path_pt, node);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret<=0 || (*node)==NULL) {
   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
               bit3= return root directory as "/" and not as ""
               bit4= (with bit2) determine type of disk file eff_path
                     and return 0 if not existing
               bit5= (with bit3) this is not a parameter
               bit6= insist in having an ISO image, even with bits1+2
   @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;

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

 eff_path[0]= 0;
 if(img_path[0]==0) {
   if(flag&8)
     strcpy(eff_path, "/");
   return(2); /* root directory */
 }

 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) {
   if(flag&8)
     strcpy(eff_path, "/");
   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);
   }
 }
 if(flag&16) {
   ret= Sfile_type(eff_path,
    1|(4*(xorriso->do_follow_links || (xorriso->do_follow_param && !(flag&32)))
                  ));
   if(ret<0)
     return(0);
   if(ret==2)
     is_dir= 1;
 }
 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
*/
int Xorriso_node_get_dev(struct XorrisO *xorriso, IsoNode *node,
                         char *path, dev_t *dev, int flag)
{
 *dev= iso_special_get_dev((IsoSpecial *) node);
 return(1);
}


/* @param flag bit0= *node is already valid
               bit1= add extra block for size estimation
               bit2= complain loudely if path is missing in image
               bit3= stbuf is to be used without eventual ACL
               bit4= try to obtain a better st_nlink count if hardlinks
                     are enabled
*/
int Xorriso_fake_stbuf(struct XorrisO *xorriso, char *path, struct stat *stbuf,
                       IsoNode **node, int flag)
{
 int ret, min_hl, max_hl, node_idx, i;
 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, !(flag&4));
   if(ret<=0)
     *node= NULL;
 }
 if(*node==NULL)
   return(0);

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

 if(flag & 8)
   stbuf->st_mode= iso_node_get_perms_wo_acl(*node) & 07777;
 else
   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_ISCHR(*node)) {
   stbuf->st_mode|= S_IFCHR;
   Xorriso_node_get_dev(xorriso, *node, path, &(stbuf->st_rdev), 0);
 } else if(LIBISO_ISBLK(*node)) {
   stbuf->st_mode|= S_IFBLK;
   Xorriso_node_get_dev(xorriso, *node, path, &(stbuf->st_rdev), 0);
 } else if(LIBISO_ISFIFO(*node))
   stbuf->st_mode|= S_IFIFO;
 else if(LIBISO_ISSOCK(*node))
   stbuf->st_mode|= S_IFSOCK;
 else if(LIBISO_ISBOOT(*node))
   stbuf->st_mode|= Xorriso_IFBOOT;

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

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

 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);

 if(LIBISO_ISDIR(*node) || (xorriso->ino_behavior & 1) || (!(flag & 16)) ||
    xorriso->hln_array == NULL)
   return(1);

 /* Try to obtain a better link count */
 ret= Xorriso_search_hardlinks(xorriso, *node, &node_idx, &min_hl, &max_hl, 0);
 if(ret < 0)
   return(ret);
 if(ret > 0 && node_idx >= 0) {
   for(i= min_hl; i <= max_hl; i++) {
     if(i == node_idx)
   continue;
     /* Check whether node is still valid */
     if(iso_node_get_parent(xorriso->hln_array[i]) != NULL)
       stbuf->st_nlink++;
   }
 }
 return(1);
}


/* @param flag >>>  bit0= follow links (i.e. stat() rather than lstat()
               bit1= do not return -2 on severe errors
               bit2= complain loudely if path is missing in image
*/
int Xorriso_iso_lstat(struct XorrisO *xorriso, char *path, struct stat *stbuf,
                      int flag)
{
 int ret;
 IsoNode *node;

 if(flag&1) {

   /* >>> follow link in ISO image */;

 }

 ret= Xorriso_fake_stbuf(xorriso, path, stbuf, &node, flag&4);
 if(ret>0)
   return(0);
 if(ret<0 && !(flag&2))
   return(-2);
 return(-1);
}


/* @param flag bit0= give directory x-permission where is r-permission
               bit1= do not transfer ACL or xattr
               bit2= record dev,inode (only if enabled by xorriso)
               bit5= transfer ACL or xattr from eventual link target
*/
int Xorriso_transfer_properties(struct XorrisO *xorriso, struct stat *stbuf,
                               char *disk_path,  IsoNode *node, int flag)
{
 mode_t mode;
 int ret= 1;
 size_t num_attrs= 0, *value_lengths= NULL;
 char **names= NULL, **values= NULL;

 mode= stbuf->st_mode;

 if((!(flag & 2)) && !(xorriso->do_aaip & 1))
   /* Will drop ACL. Update mode S_IRWXG by eventual group:: ACL entry */
   iso_local_get_perms_wo_acl(disk_path, &mode, flag & 32);

 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);

 if((xorriso->do_aaip & 5) && !(flag & 2)) {
   ret= iso_local_get_attrs(disk_path, &num_attrs, &names, &value_lengths,
                            &values, ((xorriso->do_aaip & 1) && !(flag & 2))
                                     | ((!(xorriso->do_aaip & 4)) << 2)
                                     | (flag & 32));
   if(ret < 0) { 
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_report_iso_error(xorriso, disk_path, ret,
                              "Error when obtaining local ACL and xattr", 0,
                              "FAILURE", 1 | 2);
     ret= 0; goto ex;
   }
   ret= iso_node_set_attrs(node, num_attrs, names, value_lengths, values,
                           1 | 8);
   if(ret < 0) {
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_report_iso_error(xorriso, "", ret,
                              "Error when setting ACL and xattr to image node",
                              0, "FAILURE", 1);
     ret= 0; goto ex;
   }
 }

 if((flag & 4) && ((xorriso->do_aaip & 16) || !(xorriso->ino_behavior & 2))) {
   ret= Xorriso_record_dev_inode(xorriso, disk_path, (dev_t) 0, (ino_t) 0,
                                 (void *) node, "", flag & 32);
   if(ret <= 0)
     goto ex;
 }

 ret= 1;
ex:;
 Xorriso_process_msg_queues(xorriso,0);
 iso_local_get_attrs(disk_path, &num_attrs, &names, &value_lengths,
                      &values, 1 << 15); /* free memory */
 return(ret);
}


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, stbuf_valid= 0;
 struct stat stbuf;
 char sfe[5*SfileadrL];
 off_t size= 0;

 if(lstat(disk_path, &stbuf) != -1) {
   stbuf_valid= 1;
   if(S_ISREG(stbuf.st_mode))
     size= stbuf.st_size;
 }
 if(flag&8)  {
   if(cut_size > xorriso->file_size_limit && xorriso->file_size_limit > 0) {
     sprintf(xorriso->info_text,
             "File piece exceeds size limit of %.f bytes: %.f from %s\n",
             (double) xorriso->file_size_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(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;
   } else if(size > xorriso->file_size_limit && xorriso->file_size_limit > 0) {
     sprintf(xorriso->info_text,
             "File exceeds size limit of %.f bytes: %s\n",
             (double) xorriso->file_size_limit,
             Text_shellsafe(disk_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     return(0);
   } else {
     ret= iso_tree_add_new_node(volume, dir, img_name, disk_path, node);
     if(ret<0)
       goto ex;
   }
 }

 if(stbuf_valid && ((xorriso->do_aaip & 16) || !(xorriso->ino_behavior & 2))) {
   ret= Xorriso_record_dev_inode(xorriso, disk_path,
                            stbuf.st_dev, stbuf.st_ino, (void *) *node, "", 1);
   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);
 }
 if(LIBISO_ISREG(*node))
   xorriso->pacifier_byte_count+= iso_file_get_size((IsoFile *) *node);
 return(1);
}


/* @param flag bit0= recursion is active
               bit1= do not report added files
               bit6= do not delete eventually existing node from di_array
               bit7= no special handling of split file directories
*/
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;
 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;
 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= calloc(SfileadrL, 1);
 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;}
 }

 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 */
   ret= Xorriso_path_is_excluded(xorriso, disk_path, 0); /* (is never param) */
   if(ret<0)
     {ret= -1; goto ex;}
   if(ret>0)
 continue;

   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;} 
       ret= Xorriso_resolve_link(xorriso, srcpt, link_target, 1);
       if(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 && !(flag & 128))
       target_is_split= Xorriso_is_split(xorriso, "", (void *) node, 1 | 2);

     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 | (flag & 64));
         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) {
     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_set_change_pending(xorriso, 0);
   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 | 64 | 128)));
     }
     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:
 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);
 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_implicit_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, nfd, (IsoNode *) dir,
                             ((flag&1) && d==0) | 4 | 32);
 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
          bit2= record dev,inode (only if enabled by xorriso)
*/
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(flag & 1) {
   if(stat(disk_path, &stbuf)==-1)
     return(0);
 } else {
   if(lstat(disk_path, &stbuf)==-1)
     return(0);
 }
 Xorriso_transfer_properties(xorriso, &stbuf, disk_path, node,
                           ((flag & 2) >> 1) | ((flag & 1) << 5) | (flag & 4));
 Xorriso_set_change_pending(xorriso, 0);
 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, -not_*: this is not a command parameter
               bit3= use offset and cut_size for cut_out_node
               bit4= return 3 on rejection by exclusion or user
               bit5= if directory then do not add sub tree
               bit6= do not delete eventually existing node from di_array
               bit7= no special handling of split file directories
   @return <=0 = error , 1 = added simple node , 2 = added directory ,
                         3 = rejected 
*/
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;

 ret= Xorriso_path_is_excluded(xorriso, disk_path, !(flag&4));
 if(ret<0)
   return(ret);
 if(ret>0)
   return(3*!!(flag&16));

 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 && !(flag & 128))
       target_is_split= Xorriso_is_split(xorriso, "", (void *) node, 1 | 2);

     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 | (flag & 64));
         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(3*!!(flag&16));
         }
         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_set_change_pending(xorriso, 0);
     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_implicit_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, disk_path,
                                   (IsoNode *) dir, 4 | 32);
       if(!(flag&32)) {
         ret= Xorriso_add_tree(xorriso, dir, img_path, disk_path, NULL,
                               flag & (2 | 64 | 128));
         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_set_change_pending(xorriso, 0);
       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);
 ret= Xorriso_path_is_excluded(xorriso, disk_path, !(flag&1));
 if(ret!=0)
   return(0);

 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);
 if(ret<=0)
   return(ret);

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


/* @param flag bit0= in_node is valid, do not resolve img_path
               bit1= test mode: print DEBUG messages
   @return  <0 = error,
             0 = surely not identical regular files ,
             1 = surely identical
             2 = potentially depending on unknown disk file (e.g. -cut_out)
*/
int Xorriso_restore_is_identical(struct XorrisO *xorriso, void *in_node,
                                 char *img_path, char *disk_path,
                                 char type_text[5], int flag)
{
 int ret;
 unsigned int fs_id;
 dev_t dev_id;
 ino_t ino_id;
 IsoStream *stream;
 IsoImage *volume;
 IsoNode *node;
 struct stat stbuf;
 int dummy;

 memset(type_text, 0, 5);
 if(!xorriso->volset_change_pending)
   return(0);
 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, img_path, &node, 1);
   if(ret<=0)
     return(-1);
 }
 ret= Xorriso__file_start_lba(node, &dummy, 0);
 if(ret != 0) {
   Xorriso_process_msg_queues(xorriso, 0);
   return(0);
 }
 if(!LIBISO_ISREG(node))
   return(0);
 stream= iso_file_get_stream((IsoFile *) node);
 memcpy(type_text, stream->class->type, 4);
 iso_stream_get_id(stream, &fs_id, &dev_id, &ino_id);
 if(flag&2) {
   sprintf(xorriso->info_text, "%s : fs=%d  dev=%.f  ino=%.f  (%s)",
           img_path, fs_id, (double) dev_id, (double) ino_id, type_text);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 }
 ret= stat(disk_path, &stbuf);
 if(ret==-1)
   return(0);
 if(flag&2) {
   sprintf(xorriso->info_text, "%s :       dev=%.f  ino=%.f",
           disk_path, (double) stbuf.st_dev, (double) stbuf.st_ino);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 }
 if(fs_id!=1)
   return(2);

 /* >>> obtain underlying dev_t ino_t of type "cout" */;

 if(strcmp(type_text, "fsrc")!=0)
   return(2);
 if(stbuf.st_dev==dev_id && stbuf.st_ino==ino_id)
   return(1);
 return(0);
}


/* @param flag bit0= minimal transfer: access permissions only
               bit1= keep directory open: keep owner, allow rwx for owner
                     and push directory onto xorriso->perm_stack
*/
int Xorriso_restore_properties(struct XorrisO *xorriso, char *disk_path,
                                IsoNode *node, int flag)
{
 int ret, is_dir= 0;
 mode_t mode;
 uid_t uid;
 gid_t gid;
 struct utimbuf utime_buffer;
 char sfe[5*SfileadrL];
 struct stat stbuf;
 size_t num_attrs= 0, *value_lengths= NULL;
 char **names= NULL, **values= NULL;

 ret= lstat(disk_path, &stbuf);
 if(ret==-1) {
   sprintf(xorriso->info_text,
           "Cannot obtain properties of disk file %s",
           Text_shellsafe(disk_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   {ret= 0; goto ex;}
 }
 uid= stbuf.st_uid;

 is_dir= S_ISDIR(stbuf.st_mode);

 mode= iso_node_get_permissions(node);

 if(xorriso->do_aaip & (2 | 8 | 16)) {
   ret= iso_node_get_attrs(node, &num_attrs, &names, &value_lengths, &values, 
           (!!(xorriso->do_aaip & 2)) | (!(xorriso->do_aaip & (8 | 16))) << 2);
   if (ret < 0) {
     strcpy(xorriso->info_text, "Error with obtaining ACL and xattr for ");
     Text_shellsafe(disk_path, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     {ret= 0; goto ex;}
   }
   if(num_attrs > 0) {
     ret= iso_local_set_attrs(disk_path, num_attrs, names, value_lengths,
                              values, 0);
     if(ret < 0) {
       sprintf(xorriso->info_text,
               "Cannot change ACL or xattr of disk file %s",
               Text_shellsafe(disk_path, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0);
       {ret= 0; goto ex;}
     }
   }
   Xorriso_process_msg_queues(xorriso,0);
 }
 if(!(xorriso->do_aaip & 2))
   mode= iso_node_get_perms_wo_acl(node);

 if(is_dir && (flag&2)) {
   ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node,
                           1 | ((!!(xorriso->do_aaip & 2)) << 3));
   if(ret<=0)
     {ret= 0; goto ex;}
   ret= Permstack_push(&(xorriso->perm_stack), disk_path, &stbuf, 0);
   if(ret<=0) {
     Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
     strcpy(xorriso->info_text,
            "Cannot memorize permissions for disk directory");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
     {ret= -1; goto ex;}
   }
   mode|= S_IRUSR|S_IWUSR|S_IXUSR;
 }
 ret= chmod(disk_path, mode);
 if(ret==-1) {
   sprintf(xorriso->info_text,
           "Cannot change access permissions of disk file %s",
           Text_shellsafe(disk_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   {ret= 0; goto ex;}
 }

 if(flag&1)
   {ret= 1; goto ex;}

 gid= iso_node_get_gid(node);
 if(!(S_ISDIR(stbuf.st_mode) && (flag&2)))
   uid= iso_node_get_uid(node);
 chown(disk_path, uid, gid); /* don't complain if it fails */
 utime_buffer.actime= iso_node_get_atime(node);
 utime_buffer.modtime= iso_node_get_mtime(node);
 ret= utime(disk_path,&utime_buffer);
 if(ret==-1) {
   sprintf(xorriso->info_text,
           "Cannot change atime, mtime of disk file %s",
           Text_shellsafe(disk_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   {ret= 0; goto ex;}
 }
 ret= 1;
ex:;
 iso_node_get_attrs(node, &num_attrs, &names, &value_lengths, &values,1 << 15);
 return(ret);
}


/* @param flag
               bit1= minimal transfer: access permissions only
               bit2= keep directory open: keep owner, allow rwx for owner
                     push to xorriso->perm_stack
*/
int Xorriso_restore_implicit_properties(struct XorrisO *xorriso,
          char *full_disk_path, char *disk_path, char *full_img_path, int flag)
{
 int ret, nfic, ndc, nfdc, d, i;
 char nfi[SfileadrL], nd[SfileadrL], nfd[SfileadrL], *cpt;
 char sfe[5*SfileadrL];
 struct stat stbuf;
 IsoNode *node;

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, full_disk_path, nfd,
                                 1|2|4);
 if(ret<=0)
   return(ret);
 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, nd, 1|2);
 if(ret<=0)
   return(ret);
 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi, full_img_path, nfi,
                                 1|2);
 if(ret<=0)
   return(ret);
 nfdc= Sfile_count_components(nfd, 0);
 ndc= Sfile_count_components(nd, 0);
 nfic= Sfile_count_components(nfi, 0);
 d= nfdc-ndc;
 if(d<0)
   return(-1);
 if(d>nfic)
   return(0);
 for(i= 0; i<d; i++) {
   cpt= strrchr(nfi, '/');
   if(cpt==NULL)
     return(-1); /* should not happen */
   *cpt= 0;
 }
 if(nfi[0]==0)
   strcpy(nfi, "/");
 ret= Xorriso_fake_stbuf(xorriso, nfi, &stbuf, &node, 0);
 if(ret<=0)
   return(0);
 ret= Xorriso_restore_properties(xorriso, nd, node, ((flag>>1)&3));
 if(ret<=0)
   return(ret);
 sprintf(xorriso->info_text,
         "Restored properties for %s", Text_shellsafe(nd, sfe, 0));
 sprintf(xorriso->info_text+strlen(xorriso->info_text),
         " from %s", Text_shellsafe(nfi, sfe, 0));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 return(1);
}


int Xorriso_is_plain_image_file(struct XorrisO *xorriso, void *in_node,
                                char *path, int flag)
{
 int ret, lba;
 IsoStream *stream;
 IsoNode *node;

 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret<=0)
     return(ret);
 }

 ret= Xorriso__file_start_lba(node, &lba, 0);
 if(ret > 0) { /* Stream source is from loaded image */
   stream= iso_file_get_stream((IsoFile *) node);
   if(stream != NULL)
     if(iso_stream_get_input_stream(stream, 0) == NULL)
       return(1);
 }
 return(0);
}


/* @param flag bit0= Minimal transfer: access permissions only
               bit1= *_offset and bytes are valid for writing to regular file
               bit2= This is not a parameter. Do not report if ignored
               bit3= do not restore properties
               bit4= issue pacifier messages with long lasting copying
               bit7= return 4 if restore fails from denied permission
                     do not issue error message
   @return <0 severe error , 0 failure , 1 success ,
           2 regularly not installed (disallowed device, UNIX domain socket)
           4 with bit7: permission to restore was denied
*/
int Xorriso_tree_restore_node(struct XorrisO *xorriso, IsoNode *node,
                              char *img_path, off_t img_offset,
                              char *disk_path, off_t disk_offset, off_t bytes,
                              int flag)
{
 int ret= 0, write_fd= -1, wanted, wret, open_flags, l_errno= 0;
 int target_deleted= 0;
 char *what= "[unknown filetype]", sfe[5*SfileadrL], sfe2[5*SfileadrL];
 char buf[32*1024], type_text[5], temp_path[SfileadrL], *buf_pt;
 char *link_target, *open_path_pt= NULL;
 off_t todo= 0, size, seek_ret, last_p_count= 0, already_done, read_count= 0;
 void *data_stream= NULL;
 mode_t mode;
 dev_t dev= 0;
 struct stat stbuf;
 struct utimbuf utime_buffer;

 if(LIBISO_ISDIR(node)) {
   what= "directory";
   ret= mkdir(disk_path, 0777);
   l_errno= errno;

 } else if(LIBISO_ISREG(node)) {
   what= "regular file";
   ret= Xorriso_iso_file_open(xorriso, img_path, (void *) node, &data_stream,
                              1);
   if(ret<=0)
     goto ex;
   open_path_pt= disk_path;
   ret= stat(open_path_pt, &stbuf);
   if(ret == -1 && errno == EACCES && (flag & 128))
     {ret= 4; goto ex;}
   if(flag&2) {
     if(ret!=-1 && !S_ISREG(stbuf.st_mode)) {
       sprintf(xorriso->info_text,
      "Restore offset demanded. But filesystem path leads to non-data file %s",
               Text_shellsafe(disk_path, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0);
       l_errno= 0;
       goto cannot_restore;
     }
   } else {
     /* If source and target are the same disk file then do not copy content */
     ret= Xorriso_restore_is_identical(xorriso, (void *) node, img_path,
                                       disk_path, type_text, 1);
     if(ret<0)
       goto ex;
     if(ret==1) {
       /* preliminarily emulate touch (might get overridden later) */
       utime_buffer.actime= stbuf.st_atime;
       utime_buffer.modtime= time(0);
       utime(disk_path,&utime_buffer);
       goto restore_properties;
     }
     if(ret==2) {
       /* Extract to temporary file and rename only after copying */
       ret= Xorriso_make_tmp_path(xorriso, disk_path, temp_path, &write_fd,
                                  128);
       if(ret <= 0 || ret == 4)
         goto ex;
       open_path_pt= temp_path;
     }
   }
   if(write_fd==-1) {
     open_flags= O_WRONLY|O_CREAT;
     if(disk_offset==0 || !(flag&2))
       open_flags|= O_EXCL;
     write_fd= open(open_path_pt, open_flags, S_IRUSR|S_IWUSR);
     l_errno= errno;
     if(write_fd == -1 && errno == EACCES && (flag & 128))
       {ret= 4; goto ex;}
     if(write_fd==-1)
       goto cannot_restore;
   }
   todo= size= iso_file_get_size((IsoFile *) node);
   if(flag&2) {
     if(bytes<size)
       todo= size= bytes;
     seek_ret= lseek(write_fd, disk_offset, SEEK_SET);
     l_errno= errno;
     if(seek_ret == -1) {
       sprintf(xorriso->info_text,
               "Cannot address byte %.f in filesystem path %s",
               (double) disk_offset, Text_shellsafe(open_path_pt, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0);
       goto cannot_restore;
     }
   }
   while(todo>0) {
     wanted= sizeof(buf);
     if(wanted>todo)
       wanted= todo;
     ret= Xorriso_iso_file_read(xorriso, data_stream, buf, wanted, 0);
     if(ret<=0) {
       if(xorriso->extract_error_mode == 0 &&
          Xorriso_is_plain_image_file(xorriso, node, "", 0)) {
         close(write_fd);
         write_fd= -1;
         already_done= (size - todo) / (off_t) 2048;
         already_done*= (off_t) 2048;
         sprintf(xorriso->info_text,
                 "Starting best_effort handling on ISO file %s at byte %.f",
                 Text_shellsafe(img_path, sfe, 0), (double) already_done);
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
         ret= Xorriso_read_file_data(xorriso, node, img_path, open_path_pt,
                           already_done, already_done, size - already_done, 2);
         if(ret >= 0)
           xorriso->pacifier_byte_count+= todo;
         if(ret > 0)
           todo= 0;
         else
           todo= -1;
       }
       if(ret <= 0) {
         sprintf(xorriso->info_text, "Cannot read all bytes from ISO file %s",
                 Text_shellsafe(img_path, sfe, 0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       }
   break;
     }
     read_count+= ret;
     buf_pt= buf;

     if(img_offset > read_count - ret) {
       /* skip the desired amount of bytes */
       if(read_count <= img_offset)
   continue;
       buf_pt= buf + (img_offset - (read_count - ret));
       ret= read_count - img_offset;
     }

     wret= write(write_fd, buf_pt, ret);
     if(wret>=0) {
       todo-= wret;
       xorriso->pacifier_byte_count+= wret;
       if((flag&16) &&
          xorriso->pacifier_byte_count - last_p_count >= 128*1024) {
         Xorriso_pacifier_callback(xorriso, "files restored",
                             xorriso->pacifier_count,
                             xorriso->pacifier_total, "", 2|4);
         last_p_count= xorriso->pacifier_byte_count;
       }
     }
     if(wret != ret) {
       sprintf(xorriso->info_text,
               "Cannot write all bytes to disk filesystem path %s",
               Text_shellsafe(open_path_pt, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0);
   break;
     }
   }  
   if(write_fd > 0)
     close(write_fd);
   write_fd= -1;
   if(todo > 0 && xorriso->extract_error_mode == 2 && open_path_pt != NULL) {
     unlink(open_path_pt);
     target_deleted= 1;
   }
   Xorriso_iso_file_close(xorriso, &data_stream, 0);
   data_stream= NULL;
   if(temp_path==open_path_pt && !target_deleted) {
     ret= rename(temp_path, disk_path);
     if(ret==-1) {
       sprintf(xorriso->info_text,
               "Cannot rename temporary path %s to final disk path %s",
               Text_shellsafe(temp_path, sfe, 0),
               Text_shellsafe(disk_path, sfe2, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0);
       unlink(temp_path);
       ret= 0; goto ex;
     }
   }
   ret= -(todo > 0);
   l_errno= 0;

 } else if(LIBISO_ISLNK(node)) {
   what= "symbolic link";
   link_target= (char *) iso_symlink_get_dest((IsoSymlink *) node);
   ret= symlink(link_target, disk_path);
   l_errno= errno;

 } else if(LIBISO_ISCHR(node)) {
   what= "character device";
   if(xorriso->allow_restore!=2) {
ignored:;
     if(!(flag&4)) {
       sprintf(xorriso->info_text, "Ignored file type: %s %s = %s", what,
             Text_shellsafe(img_path,sfe,0), Text_shellsafe(disk_path,sfe2,0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     }
     {ret= 2; goto ex;}
   }
   mode= S_IFCHR | 0777;
   ret= Xorriso_node_get_dev(xorriso, node, img_path, &dev, 0);
   if(ret<=0)
     goto ex;
   if(dev == (dev_t) 1) {
probably_damaged:;
     sprintf(xorriso->info_text,
            "Most probably damaged device file not restored: mknod %s %s 0 1",
            Text_shellsafe(disk_path, sfe, 0), LIBISO_ISCHR(node) ? "c" : "b");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
     ret= 0; goto ex;
   }
   ret= mknod(disk_path, mode, dev);
   l_errno= errno;

 } else if(LIBISO_ISBLK(node)) {
   what= "block device";
   if(xorriso->allow_restore!=2)
     goto ignored;
   mode= S_IFBLK | 0777;
   ret= Xorriso_node_get_dev(xorriso, node, img_path, &dev, 0);
   if(ret<=0)
     goto ex;
   if(dev == (dev_t) 1)
     goto probably_damaged;
   ret= mknod(disk_path, mode, dev);
   l_errno= errno;

 } else if(LIBISO_ISFIFO(node)) {
   what= "named pipe";
   mode= S_IFIFO | 0777;
   ret= mknod(disk_path, mode, dev);
   l_errno= errno;

 } else if(LIBISO_ISSOCK(node)) {
   what= "unix socket";
   /* Restoring a socket file is not possible. One rather needs to restart
      the service which temporarily created the socket. */
   goto ignored;

 } else {
   sprintf(xorriso->info_text, "Cannot restore file type '%s'", what);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0);
   ret= 0; goto ex;
 }
 if(ret == -1 && l_errno == EACCES && (flag & 128))
   {ret= 4; goto ex;}
 if(ret==-1) {
cannot_restore:;
   sprintf(xorriso->info_text,
           "Cannot restore %s to disk filesystem: %s",
           what, Text_shellsafe(img_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, l_errno, "FAILURE", 0);
   ret= 0; goto ex;
 }

restore_properties:;
 if((flag&8) || LIBISO_ISLNK(node))
   ret= 1;
 else
   ret= Xorriso_restore_properties(xorriso, disk_path, node, flag&1);
 if(todo < 0)
   ret= 0;
ex:;
 if(write_fd >= 0) {
   close(write_fd);
   if(ret <= 0 && xorriso->extract_error_mode == 2 && open_path_pt != NULL)
     unlink(open_path_pt);
 }
 if(data_stream!=NULL)
   Xorriso_iso_file_close(xorriso, &data_stream, 0);
 return(ret);
}


/* Handle overwrite situation in disk filesystem.
   @param node  intended source of overwriting or NULL
   @param flag
               bit4= return 3 on rejection by exclusion or user
               bit6= permission to call Xorriso_make_accessible()
*/
int Xorriso_restore_overwrite(struct XorrisO *xorriso,
                              IsoNode *node, char *img_path,
                              char *path, char *nominal_path,
                              struct stat *stbuf, int flag)
{
 int ret;
 char sfe[5*SfileadrL], sfe2[5*SfileadrL], type_text[5];

 Xorriso_process_msg_queues(xorriso,0);
 if(xorriso->do_overwrite==1 ||
    (xorriso->do_overwrite==2 && !S_ISDIR(stbuf->st_mode))) {

   ret= Xorriso_restore_is_identical(xorriso, (void *) node, img_path,
                                     path, type_text, (node!=NULL));
   if(ret<0)
     return(ret);
   if(ret>0) /* will be handled properly by restore functions */
     ret= Xorriso_reassure_restore(xorriso, path, 8);
   else
     ret= Xorriso_rmx(xorriso, (off_t) 0, path, 8 | (flag & 64));
   if(ret<=0)
     return(ret);
   if(ret==3) {
     sprintf(xorriso->info_text, "User revoked restoring of (ISO) file: %s",
             Text_shellsafe(img_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     return(3*!!(flag&16));
   }
   return(1);
 }
 Xorriso_msgs_submit(xorriso, 0, nominal_path, 0, "ERRFILE", 0);
 sprintf(xorriso->info_text,
         "While restoring %s : %s exists and may not be overwritten",
         Text_shellsafe(nominal_path, sfe, 0), strcmp(nominal_path, path)==0 ?
                                "file object" : Text_shellsafe(path, sfe2, 0));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
 return(0);
}


/* @param flag bit0= do not accept hln_targets[i] != NULL as *node_idx
               bit1= use *node_idx as found index rather than searching it
               bit2= with bit1: use xorriso->node_array rather than hln_array
*/
int Xorriso_search_hardlinks(struct XorrisO *xorriso, IsoNode *node,
                             int *node_idx, int *min_hl, int *max_hl, int flag)
{
 int idx, ret, i, node_count;
 void *np, **node_array;

 node_array= xorriso->hln_array;
 node_count= xorriso->hln_count;
 *min_hl= *max_hl= -1;
 np= node;
 if(flag & 2) {
   idx= *node_idx;
   if(flag & 4) {
     node_array= xorriso->node_array;
     node_count= xorriso->node_counter;
   }
 } else {
   *node_idx= -1;
   ret= Xorriso_search_in_hln_array(xorriso, np, &idx, 0);
   if(ret <= 0)
     return(ret);
 }
 for(i= idx - 1; i >= 0 ; i--)
   if(Xorriso__findi_sorted_ino_cmp(&(node_array[i]), &np) != 0)
 break;
 *min_hl= i + 1;
 for(i= idx + 1; i < node_count; i++) 
   if(Xorriso__findi_sorted_ino_cmp(&(node_array[i]), &np) != 0)
 break;
 *max_hl= i - 1;

 /* Search for *node_idx */
 if(flag & 2)
   return(1);
 for(i= *min_hl; i <= *max_hl; i++)
   if(node_array[i] == np) {
     if((flag & 1) && xorriso->hln_targets != NULL && !(flag & 4))
       if(xorriso->hln_targets[i] != NULL)
 continue;
     *node_idx= i;
 break;
   }
 return(1);
}


/*
   @return <0 error,
           bit0= hardlink created
           bit1= siblings with target NULL found
           bit2= siblings with non-NULL target found
*/
int Xorriso_restore_target_hl(struct XorrisO *xorriso, IsoNode *node,
                           char *disk_path, int *node_idx, int flag)
{
 int ret, min_hl, max_hl, i, null_target_sibling= 0, link_sibling= 0;

 if(xorriso->hln_targets == NULL)
   return(0);
 ret= Xorriso_search_hardlinks(xorriso, node, node_idx, &min_hl, &max_hl, 1);
 if(ret < 0)
   return(ret);
 if(ret == 0 || *node_idx < 0 || min_hl == max_hl)
   return(0);
 for(i= min_hl; i <= max_hl; i++) {
   if(xorriso->hln_targets[i] == NULL) {
     if(i != *node_idx)
       null_target_sibling= 1;
 continue;
   }
   link_sibling= 1;
   ret= Xorriso_restore_make_hl(xorriso, xorriso->hln_targets[i], disk_path,
                                !!xorriso->do_auto_chmod);
   if(ret > 0)
     return(1);
 }
 return((null_target_sibling << 1) | (link_sibling << 2));
}


/*
   @return <0 error,
           bit0= hardlink created
           bit2= siblings lower index found
*/
int Xorriso_restore_prefix_hl(struct XorrisO *xorriso, IsoNode *node,
                              char *disk_path, int node_idx, int flag)
{
 int ret, min_hl, max_hl, i, link_sibling= 0, hflag;
 char old_path[SfileadrL], img_path[SfileadrL];
 struct Xorriso_lsT *img_prefixes= NULL, *disk_prefixes= NULL;

 ret= Xorriso_search_hardlinks(xorriso, node, &node_idx, &min_hl, &max_hl,
                               2 | 4);
 if(ret < 0)
   return(ret);
 if(ret == 0 || min_hl == max_hl)
   return(0);

 for(i= min_hl; i < node_idx; i++) {
   link_sibling= 1;
   ret= Xorriso_path_from_node(xorriso, xorriso->node_array[i], img_path, 0);
   if(ret < 0)
     return(ret);
   if(ret == 0)
 continue; /* Node is deleted from tree (Should not happen here) */
   hflag= 1;
   if(i == min_hl) {
     hflag= 0;
   } else if(xorriso->node_array[i] != xorriso->node_array[i - 1]) {
     hflag= 0;
   }
   if(hflag == 0) {
     img_prefixes= xorriso->node_img_prefixes;
     disk_prefixes= xorriso->node_disk_prefixes;
   }
   ret= Xorriso_make_restore_path(xorriso, &img_prefixes, &disk_prefixes,
                                  img_path, old_path, hflag);
   if(ret <= 0)
     return(ret);
   ret= Xorriso_restore_make_hl(xorriso, old_path, disk_path,
                                !!xorriso->do_auto_chmod);
   if(ret > 0)
     return(1);
 }
 return(link_sibling << 2);
}


/* @return <0 = error , 0 = availmem exhausted first time , 1 = ok
                        2 = availmem exhausted repeated
*/ 
int Xorriso_register_node_target(struct XorrisO *xorriso, int node_idx,
                                 char *disk_path, int flag)
{
 int l;

 if(xorriso->node_targets_availmem == 0)
   return(2);
 if(xorriso->hln_targets == NULL || node_idx < 0 ||
    node_idx >= xorriso->hln_count)
   return(0);
 if(xorriso->hln_targets[node_idx] != NULL) {
   xorriso->node_targets_availmem+= strlen(xorriso->hln_targets[node_idx]) +1;
   free(xorriso->hln_targets[node_idx]);
 }
 l= strlen(disk_path);
 if(xorriso->node_targets_availmem <= l + 1) {
   sprintf(xorriso->info_text,
 "Hardlink target buffer exceeds -temp_mem_limit. Hardlinks may get divided.");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   xorriso->node_targets_availmem= 0;
   return(0);
 }
 xorriso->hln_targets[node_idx]= strdup(disk_path);
 if(xorriso->hln_targets[node_idx] == NULL) {
   Xorriso_no_malloc_memory(xorriso, NULL, 0);
   return(-1);
 }
 xorriso->node_targets_availmem-= (l + 1);
 return(1);
}


/*
   @param flag bit0= offset and bytes is valid for writing to regular file
               bit1= do not report copied files
               bit2= -follow, -not_*: this is not a command parameter
               bit3= keep directory open: keep owner, allow rwx for owner
               bit4= do not look for hardlinks even if enabled
               bit6= this is a copy action: do not fake times and ownership
               bit7= return 4 if restore fails from denied permission
                     do not issue error message
   @return <=0 = error , 1 = added leaf file object , 2 = added directory ,
             3= regularly not installed (disallowed device, UNIX domain socket)
             4 = with bit7: permission to restore was denied
*/
int Xorriso_restore_disk_object(struct XorrisO *xorriso,
                                char *img_path, IsoNode *node,
                                char *disk_path,
                                off_t offset, off_t bytes, int flag)
{
 int ret, i, split_count= 0, partno, total_parts, leaf_is_split= 0;
 int record_hl_path= 0, node_idx, cannot_register= 0;
 off_t total_bytes, was_byte_count;
 char *part_name, part_path[SfileadrL], *img_path_pt;
 char sfe[5*SfileadrL], sfe2[5*SfileadrL];
 IsoImage *volume;
 IsoNode *part_node, *first_part_node= NULL;
 struct SplitparT *split_parts= NULL;
 struct stat stbuf;

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

 was_byte_count= xorriso->pacifier_byte_count;
  
 if(LIBISO_ISDIR(node) && xorriso->do_concat_split)
   leaf_is_split= Xorriso_identify_split(xorriso, img_path, node,
                                      &split_parts, &split_count, &stbuf, 1|2);
 if(leaf_is_split) {
   /* map all files in directory img_path into regular file disk_path */

   for(i=0 ; i<split_count; i++) {
     Splitparts_get(split_parts, i, &part_name, &partno, &total_parts,
                    &offset, &bytes, &total_bytes, 0);

     strcpy(part_path, img_path);
     if(Sfile_add_to_path(part_path, part_name, 0)<=0) {
       Xorriso_much_too_long(xorriso, strlen(img_path)+strlen(part_name)+1, 2);
       goto restoring_failed;
     }
     ret= Xorriso_node_from_path(xorriso, volume, part_path, &part_node, 0);
     if(ret<=0)
       goto restoring_failed;
     if(i==0)
       first_part_node= part_node;
     if(offset+bytes>total_bytes)
             bytes= total_bytes-offset;
     ret= Xorriso_tree_restore_node(xorriso, part_node, part_path, (off_t) 0,
               disk_path, offset, bytes,
               (!!(flag&64)) | 2 | (flag & (4 | 128)) | 8 | ( 16 * !(flag&2)));
     if(ret<=0)
       goto restoring_failed;
     if(ret == 4)
       goto ex;
   }
   if(first_part_node!=NULL)
     Xorriso_restore_properties(xorriso, disk_path, first_part_node,
                                !!(flag&64));
   goto went_well;
 }

#ifdef Osirrox_not_yeT

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

#endif /* Osirrox_not_yeT */

   img_path_pt= img_path;

 if(!((xorriso->ino_behavior & 4) || (flag & (1 | 16)) || LIBISO_ISDIR(node))){
   /* Try to restore as hardlink */
   ret= Xorriso_restore_target_hl(xorriso, node, disk_path, &node_idx,
                                  !!xorriso->do_auto_chmod);
   if(ret < 0) {
     goto ex;
   } else if(ret & 1) {
     /* Success, hardlink was created */
     goto went_well;
   } else if(ret & 2) {
     /* Did not establish hardlink. Hardlink siblings with target NULL found.*/
     record_hl_path= 1;
   }
   if(ret & 4) {
     /* Found siblings with non-NULL target, but did not link. */
     ret= Xorriso_eval_problem_status(xorriso, 1, 0);
     if(ret < 0)
       {ret= 0; goto ex;}
   }
 }

 ret= Xorriso_tree_restore_node(xorriso, node, img_path_pt, (off_t) 0,
                                disk_path, offset, bytes,
     (flag&(4 | 8 | 128)) | (!!(flag&64)) | ((flag&1)<<1) | ( 16 * !(flag&2)));
 if(ret == 4)
   goto ex;
 if(ret>0 && (flag&8))
   ret= Xorriso_restore_properties(xorriso, disk_path, node, 2 | !!(flag&64));
 if(ret<=0) {
restoring_failed:;
   sprintf(xorriso->info_text, "Restoring 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);
   {ret= 0; goto ex;}
 }
 if(ret==2)
   {ret= 3; goto ex;}
 if(record_hl_path) { /* Start of a disk hardlink family */
   ret= Xorriso_register_node_target(xorriso, node_idx, disk_path, 0);
   if(ret < 0)
     goto ex;
   if(ret == 0)
     cannot_register= 1;
 }

went_well:;
 xorriso->pacifier_count++;
 if(!(flag&2))
   Xorriso_pacifier_callback(xorriso, "files restored",
                             xorriso->pacifier_count,
                             xorriso->pacifier_total, "", 4);
 ret= 1;
ex:;
 if(split_parts!=NULL) 
   Splitparts_destroy(&split_parts, split_count, 0);
 if(ret > 0 && cannot_register)
   ret= 0;
 return(ret);
}


/* @param flag bit0= source is a directory and not to be restored as split file
           >>> bit6= permission to call Xorriso_make_accessible()
   @return <=0 error , 1=collision handled , 2=no collision , 3=revoked by user
*/
int Xorriso_handle_collision(struct XorrisO *xorriso,
                             IsoNode *node, char *img_path,
                             char *disk_path, char *nominal_disk_path,
                             int *stbuf_ret, int flag)
{
 int ret, target_is_dir= 0, target_is_link= 0, stat_ret, made_accessible= 0;
 struct stat target_stbuf, lt_stbuf;
 struct PermiteM *perm_stack_mem;

 perm_stack_mem= xorriso->perm_stack;

 /* does a disk file exist with this name ? */
 *stbuf_ret= lstat(disk_path, &target_stbuf);
 if(*stbuf_ret==-1) {
   if((flag & 64) && errno == EACCES) {
     ret= Xorriso_make_accessible(xorriso, disk_path, 0);
     if(ret < 0)
       goto ex;
     made_accessible= 1;
     *stbuf_ret= lstat(disk_path, &target_stbuf);
   }
   if(*stbuf_ret==-1)
     {ret= 2; goto ex;}
 }
 target_is_link= S_ISLNK(target_stbuf.st_mode);
 if(target_is_link) {
   stat_ret= stat(disk_path, &lt_stbuf);
   if(stat_ret == -1) {
     if((flag & 64) && errno == EACCES && !made_accessible) {
       ret= Xorriso_make_accessible(xorriso, disk_path, 0);
       if(ret < 0)
         goto ex;
       made_accessible= 1;
       stat_ret= stat(disk_path, &lt_stbuf);
     }
   }
   if(stat_ret != -1)
     target_is_dir= S_ISDIR(lt_stbuf.st_mode);
 } else {
   target_is_dir= S_ISDIR(target_stbuf.st_mode);
 }
 if(target_is_dir && (!target_is_link) && !(flag&1)) {
   strcpy(xorriso->info_text, "Attempt to replace DISK directory ");
   Text_shellsafe(nominal_disk_path,
                  xorriso->info_text+strlen(xorriso->info_text), 0);
   strcat(xorriso->info_text, " by ISO file ");
   Text_shellsafe(img_path, xorriso->info_text+strlen(xorriso->info_text), 0);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }

 if(!(target_is_dir && (flag&1))) {
   Xorriso_process_msg_queues(xorriso,0);
   ret= Xorriso_restore_overwrite(xorriso, node, img_path, disk_path,
                           nominal_disk_path, &target_stbuf, 16 | (flag & 64));
   if(ret==3)
     {ret= 3; goto ex;}
   if(ret<=0) 
     goto ex;
   *stbuf_ret= -1; /* It might still exist but will be handled properly */
 }
 ret= 1;
ex:;
 if(made_accessible)
   Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0);
 return(ret);
}


/* @param flag bit0= recursion is active
               bit1= do not report restored files
               bit6= this is a copy action: do not fake times and ownership
               bit8= only register non-directory nodes in xorriso->node_array
               bit7+8=
                     0= direct operation
                     1= create only directories,
                        count nodes in xorriso->node_counter
                     2= only register non-directory nodes in
                        xorriso->node_array
                     3= count nodes in xorriso->node_counter,
                        create no directory
*/
int Xorriso_restore_tree(struct XorrisO *xorriso, IsoDir *dir,
                         char *img_dir_path, char *disk_dir_path,
                         off_t boss_mem,
                         struct LinkiteM *link_stack, int flag)
{
 IsoImage *volume;
 IsoNode *node;
 IsoDirIter *iter= NULL;
 IsoNode **node_array= NULL;
 int node_count, node_idx;
 int ret, source_is_dir, source_is_link, fret, was_failure= 0;
 int do_not_dive, source_is_split= 0, len_dp, len_ip, stbuf_ret, hflag, hret;
 char *name, *disk_name, *leaf_name, *srcpt, *stbuf_src= "";
 struct LinkiteM *own_link_stack;
 char *sfe= NULL, *sfe2= NULL;
 char *disk_path= NULL, *img_path= NULL, *link_target= NULL;
 off_t mem;
 struct PermiteM *perm_stack_mem;
 struct stat stbuf;
 int dir_create= 0, node_register= 0, do_node_count= 0, normal_mode= 0;

 perm_stack_mem= xorriso->perm_stack;
 switch((flag >> 7) & 3) {
 case 0: normal_mode= 1;
 break; case 1: dir_create= 1;
 break; case 2: node_register= 1;
 break; case 3: do_node_count= 1;
 }

 /* 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;}
 }

 own_link_stack= link_stack;

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

 stbuf_src= img_dir_path;
 node= (IsoNode *) dir;
 ret= Xorriso_fake_stbuf(xorriso, stbuf_src, &stbuf, &node, 1);
 if(ret<=0) {
   Xorriso_msgs_submit(xorriso, 0, disk_dir_path, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text,"Cannot open as (ISO) source directory: %s",
           Text_shellsafe(img_dir_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   {ret= 0; goto ex;}
 }

#ifdef Osirrox_not_yeT

 dev_t dir_dev;
 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;}
 }

#endif /* Osirrox_not_yeT */

 if(!S_ISDIR(stbuf.st_mode)) {
   Xorriso_msgs_submit(xorriso, 0, disk_dir_path, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text,"Is not a directory in ISO image: %s",
           Text_shellsafe(img_dir_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   {ret= 0; goto ex;}
 }

 mem= boss_mem;
 ret= Xorriso_findi_iter(xorriso, dir, &mem, &iter, &node_array, &node_count,
                         &node_idx, &node,
                         1 | 4 * (normal_mode && (xorriso->ino_behavior & 4)));
 if(ret<=0)
   goto ex;

 if(Sfile_str(img_path, img_dir_path,0)<=0) {
much_too_long:;
   Xorriso_much_too_long(xorriso, SfileadrL, 2);
   {ret= 0; goto ex;}
 }
 if(img_path[0]==0 || img_path[strlen(img_path)-1]!='/')
   strcat(img_path,"/");
 name= img_path+strlen(img_path);
 if(Sfile_str(disk_path, disk_dir_path, 0)<=0)
   goto much_too_long;
 if(disk_path[0]==0 || disk_path[strlen(disk_path)-1]!='/')
   strcat(disk_path,"/");
 disk_name= disk_path+strlen(disk_path);

 len_dp= strlen(disk_path);
 len_ip= strlen(img_path);

 while(1) { /* loop over ISO directory content */
   stbuf_src= "";

#ifdef Osirrox_not_yeT

   Linkitem_reset_stack(&own_link_stack, link_stack, 0);

#endif

   srcpt= img_path;
   Xorriso_process_msg_queues(xorriso,0);
   ret= Xorriso_findi_iter(xorriso, dir, &mem, &iter, &node_array, &node_count,
                           &node_idx, &node, 0);
   if(ret<0)
     goto ex;
   if(ret==0 || xorriso->request_to_abort)
 break;
   leaf_name=  (char *) iso_node_get_name(node);
   if(Xorriso_much_too_long(xorriso, len_dp + strlen(leaf_name)+1, 0)<=0)
     {ret= 0; goto was_problem;}
   if(Xorriso_much_too_long(xorriso, len_ip + strlen(leaf_name)+1, 0)<=0)
     {ret= 0; goto was_problem;}
   /* name is a pointer into img_path */
   strcpy(name, leaf_name);
   strcpy(disk_name,  leaf_name);

   stbuf_src= srcpt;
   ret= Xorriso_fake_stbuf(xorriso, img_path, &stbuf, &node, 1);
   if(ret<=0)
     goto was_problem;
   source_is_dir= 0;
   source_is_link= S_ISLNK(stbuf.st_mode);

#ifdef Osirrox_not_yeT

   /* ??? Link following in the image would cause severe problems
          with Xorriso_path_from_node() */

   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;
   }

#endif /* Osirrox_not_yeT */

   do_not_dive= 0;
   if(S_ISDIR(stbuf.st_mode))
     source_is_dir= 1;
   source_is_split= 0;
   if(source_is_dir)
     source_is_split= Xorriso_is_split(xorriso, img_path, node, 1|2);
   if(source_is_split)
     do_not_dive= 1;

   if(source_is_dir || !(dir_create || do_node_count || node_register)) {
     ret= Xorriso_handle_collision(xorriso, node, img_path,
                                   disk_path, disk_path, &stbuf_ret,
                                   (source_is_dir && !source_is_split));
     if(ret<=0 || ret==3)
       goto was_problem;
   } else {
     stbuf_ret= -1;
   }

   if(stbuf_ret!=-1) { /* (Can only happen with directory) */
     Xorriso_auto_chmod(xorriso, disk_path, 0);
   } else {
     hflag= 4 | (flag & (2|64));
     if(source_is_dir && !do_not_dive)
       hflag|= 8; /* keep directory open for user */
     if((dir_create || do_node_count) && !source_is_dir) {
       xorriso->node_counter++;
     } else if(node_register && !source_is_dir) {
       if(xorriso->node_counter < xorriso->node_array_size) {
         xorriso->node_array[xorriso->node_counter++]= (void *) node;
         iso_node_ref(node);
       }
     } else if(node_register || do_node_count) {
       ret= 1;
     } else {
       ret= Xorriso_restore_disk_object(xorriso, img_path, node, disk_path,
                                        (off_t) 0, (off_t) 0, hflag);
     }
     if(ret<=0)
       goto was_problem;
   }
   if(source_is_dir && !do_not_dive) {
     ret= Xorriso_restore_tree(xorriso, (IsoDir *) node,
                               img_path, disk_path, mem,
                               own_link_stack, 1 | (flag & (2 | (3 << 7))));
     /* eventually restore exact access permissions of directory */
     hret= Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso,
                         !!(flag&64));
     if(hret<=0 && hret<ret)
       ret= hret;
     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;
   Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, !!(flag&64));
 }

 ret= 1;
ex:
 Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, !!(flag&64));
 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);
 Xorriso_findi_iter(xorriso, dir, &mem, &iter, &node_array, &node_count,
                    &node_idx, &node, (1<<31));

 Xorriso_process_msg_queues(xorriso,0);

#ifdef Osirrox_not_yeT

 Linkitem_reset_stack(&own_link_stack, link_stack, 0);

#endif

 if(ret<=0)
   return(ret);
 return(!was_failure);
}


/*
   @param flag
           >>> bit0= mkdir: graft in as empty directory, not as copy from iso
               bit1= do not report copied files
               bit2= -follow, -not_*: this is not a command parameter
               bit3= use offset and cut_size for -paste_in
               bit4= return 3 on rejection by exclusion or user
               bit5= if directory then do not add sub tree
               bit6= this is a copy action: do not fake times and ownership
               bit7+8= operation mode
                     0= direct operation
                     1= create only directories,
                        count nodes in xorriso->node_counter
                     2= only register non-directory nodes in
                        xorriso->node_array
                     3= count nodes in xorriso->node_counter,
                        create no directory
               bit9= with operation mode 1 do net register prefixes
   @return <=0 = error , 1 = added leaf file object , 2 = added directory ,
                         3 = rejected 
*/
int Xorriso_restore(struct XorrisO *xorriso,  
                    char *img_path, char *disk_path,
                    off_t offset, off_t bytes, int flag)
{
 IsoImage *volume;
 char path[SfileadrL], *apt, *npt, sfe[5*SfileadrL];
 IsoNode *node= NULL;
 int done= 0, is_dir= 0, ret, source_is_dir, stbuf_ret, hret;
 int dir_create= 0, node_count= 0, node_register= 0;
 int leaf_is_split= 0, source_is_split= 0, new_dir_made= 0;
 struct stat stbuf;
 struct PermiteM *perm_stack_mem;

 perm_stack_mem= xorriso->perm_stack;
 switch((flag >> 7) & 3) {
 case 1: dir_create= 1;
 break; case 2: node_register= 1;
 break; case 3: node_count= 1;
 }

 if(dir_create && !(flag & (1 << 9))) {
   ret= Xorriso_lst_append_binary(&(xorriso->node_disk_prefixes),
                                  disk_path, strlen(disk_path) + 1, 0);
   if(ret <= 0)
     goto ex; 
   ret= Xorriso_lst_append_binary(&(xorriso->node_img_prefixes),
                                  img_path, strlen(img_path) + 1, 0);
   if(ret <= 0)
     goto ex; 
 }

 ret= Xorriso_path_is_excluded(xorriso, disk_path, !(flag&4));
 if(ret<0)
   goto ex;
 if(ret>0)
   {ret= 3*!!(flag&16); goto ex;}

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

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

 if(!(flag&1)) {
   ret= Xorriso_fake_stbuf(xorriso, img_path, &stbuf, &node, 0);
   if(ret>0) {
     if(S_ISDIR(stbuf.st_mode))
       is_dir= 1;

#ifdef Osirrox_not_yeT

     /* ??? this would cause severe problems with Xorriso_path_from_node() */

     else if((stbuf.st_mode&S_IFMT)==S_IFLNK &&
             (xorriso->do_follow_links ||
              (xorriso->do_follow_param && !(flag&4)))) {
       resolve_link= 1;
       ret= Xorriso_iso_lstat(xorriso, img_path, &stbuf, 1|2);
       if(ret!=-1) {
         if(S_ISDIR(stbuf.st_mode))
           is_dir= 1;
       }
     }
#endif /* Osirrox_not_yeT */

   } else {
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
     sprintf(xorriso->info_text,
            "Cannot determine attributes of (ISO) source file %s",
            Text_shellsafe(img_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
     ret= 0; goto ex;
   }
   if(is_dir && xorriso->do_concat_split)
     leaf_is_split= Xorriso_is_split(xorriso, img_path, node, 1|2);
 }
 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);
   source_is_split= done && leaf_is_split;

   stbuf_ret= -1;
   if((flag&8) && done) {

     /* ??? move down from Xorriso_paste_in() :
            check whether target does not exist or both are regular */;

   } else if(source_is_dir || !(dir_create || node_count || node_register)) {
     ret= Xorriso_handle_collision(xorriso, node, img_path, path, disk_path,
                              &stbuf_ret, (source_is_dir && !source_is_split));
     if(ret<=0 || ret==3)
       goto ex;
   }

   new_dir_made= 0;
   if(stbuf_ret==-1 && (source_is_dir && !source_is_split) &&
      !(node_count || node_register)) {
                                                         /* make a directory */
     ret= mkdir(path, 0777);
     if(ret==-1) {
       Xorriso_process_msg_queues(xorriso,0);
       Xorriso_msgs_submit(xorriso, 0, disk_path, 0, "ERRFILE", 0);
       sprintf(xorriso->info_text,
              "While restoring '%s' : could not insert '%s'", disk_path, path);
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0);
       {ret= 0; goto ex;}
     }
     if(!done) {
       /* keep rwx for the owner */
       Xorriso_restore_implicit_properties(xorriso, disk_path, path,
                                           img_path, 4);
     }
     new_dir_made= 1;
   } else if((source_is_dir && !source_is_split)) {
     if(!(node_count || node_register))
       Xorriso_auto_chmod(xorriso, path, 0);
   }
   if(done) {
attach_source:;

     if(flag&1) {
       /* directory was created above */;

     } else if(is_dir && !source_is_split) {

       if(!node_register) {
         if(new_dir_made) /* keep open and push to Permstack */
           Xorriso_restore_properties(xorriso, disk_path, node,
                                      2 | !!(flag&64));
       }
       if(!(flag&32)) {
         ret= Xorriso_restore_tree(xorriso, (IsoDir *) node, img_path, path,
                                  (off_t) 0, NULL, flag & (2 | 64 | (3 << 7)));
         if(ret<=0)
           goto ex;
         if(new_dir_made && !(flag&64))
           /* set timestamps which Permstack_pop() will not set */
           Xorriso_restore_properties(xorriso, disk_path, node, 2);
       }
     } else {
       if(dir_create || node_count) {
         xorriso->node_counter++;
       } else if(node_register) {
         if(xorriso->node_counter < xorriso->node_array_size) {
           xorriso->node_array[xorriso->node_counter++]= (void *) node;
           iso_node_ref(node);
         }
       } else {
         ret= Xorriso_restore_disk_object(xorriso, img_path, node, path,
                                offset, bytes, (flag & (2|4|64)) | !!(flag&8));
         if(ret<=0)
           goto ex;
       }
     }
   } else
     *npt= '/';
 }
 Xorriso_process_msg_queues(xorriso,0);
 ret= 1 + (is_dir && !leaf_is_split); 
ex:;
 /* restore exact access permissions of stacked paths */
 hret= Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso,
                     2 | !!(flag&64));
 if(hret<=0 && hret<ret)
   ret= hret;
 return(ret);
}


int Xorriso_restore_node_array(struct XorrisO *xorriso, int flag)
{
 int i, ret, was_failure= 0, fret, hflag, stbuf_ret, faulty_family= 0;
 struct PermiteM *perm_stack_mem;
 char img_path[SfileadrL], disk_path[SfileadrL];
 IsoNode *node;
 struct Xorriso_lsT *img_prefixes= NULL, *disk_prefixes= NULL;

 perm_stack_mem= xorriso->perm_stack;

 Xorriso_sort_node_array(xorriso, 0);

 disk_path[0]= 0;
 for(i= 0; i < xorriso->node_counter; i++) {
   node= (IsoNode *) xorriso->node_array[i];
   ret= Xorriso_path_from_node(xorriso, node, img_path, 0);
   if(ret < 0)
     goto ex;
   if(ret == 0)
 continue; /* Node is deleted from tree (Should not happen here) */
   hflag= 1;
   if(i == 0) {
     hflag= 0;
   } else if(node != xorriso->node_array[i - 1]) {
     hflag= 0;
   }
   if(hflag == 0) {
     img_prefixes= xorriso->node_img_prefixes;
     disk_prefixes= xorriso->node_disk_prefixes;
   }
   ret= Xorriso_make_restore_path(xorriso, &img_prefixes, &disk_prefixes,
                                  img_path, disk_path, hflag);
   if(ret<=0)
     goto was_problem;
 
   ret= Xorriso_handle_collision(xorriso, node, img_path, disk_path, disk_path,
                                 &stbuf_ret, 64);
   if(ret<=0 || ret==3)
     goto was_problem;
   if(xorriso->hln_array != NULL && !(xorriso->ino_behavior & 16)) {
     /* Eventual lookup of hardlinks will be done in
        Xorriso_restore_disk_object() */;
   } else if(i > 0 && !(xorriso->ino_behavior & 4)) {
     if(Xorriso__findi_sorted_ino_cmp(&(xorriso->node_array[i-1]),
                                      &(xorriso->node_array[i])) == 0) {
       if(faulty_family) {
         sprintf(xorriso->info_text, "Hardlinking omitted with ");
         Text_shellsafe(disk_path, xorriso->info_text, 1);
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0);
       } else {
         /* Try to install hardlink to a sibling */
         ret= Xorriso_restore_prefix_hl(xorriso, node, disk_path, i, 0);
         if(ret < 0) {
           goto was_problem;
         } else if(ret & 1) {
           /* Success, hardlink was created */
           xorriso->pacifier_count++;
 continue;
         }
         if(ret & 4) {
           /* Found elder siblings, but did not link. */
           ret= Xorriso_eval_problem_status(xorriso, 1, 0);
           if(ret < 0)
             {ret= 0; goto ex;}
         }
       }
     } else
       faulty_family= 0;
   }

   ret= Xorriso_restore_disk_object(xorriso, img_path, node, disk_path,
                                    (off_t) 0, (off_t) 0,
                                    4 | (xorriso->ino_behavior & 16) | 128);
   if(ret<=0)
     goto was_problem;
   if(ret == 4) {
     /* Failed from lack of permission */
     ret= Xorriso_make_accessible(xorriso, disk_path, 0);
     if(ret < 0)
       goto ex;
     ret= Xorriso_restore_disk_object(xorriso, img_path, node, disk_path,
                       (off_t) 0, (off_t) 0, 4 | (xorriso->ino_behavior & 16));
     if(ret<=0)
       goto was_problem;
     Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0);
   }

 continue; /* regular bottom of loop */
was_problem:;
   faulty_family= 1;
   was_failure= 1;
   fret= Xorriso_eval_problem_status(xorriso, ret, 1|2);
   if(fret<0)
     goto ex;
   Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0);
 }
 ret= 1;
ex:;
 Permstack_pop(&(xorriso->perm_stack), perm_stack_mem, xorriso, 0);
 return(ret);
}


/* @param flag bit0= -follow, -not: disk_path is not a command parameter
*/
int Xorriso_paste_in(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;
 IsoNode *node;

 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx, disk_path, eff_dest,
                                 2|4);
 if(ret<=0)
   return(ret);
 ret= Xorriso_path_is_excluded(xorriso, disk_path, !(flag&1));
 if(ret!=0)
   return(0);
 ret= stat(eff_dest, &stbuf);
 if(ret!=-1 && !S_ISREG(stbuf.st_mode)) {
   Xorriso_msgs_submit(xorriso, 0, eff_dest, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text,
           "-paste_in: DISK file %s exists and is not a data file",
           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_source, 2);
 if(ret<=0)
   return(ret);
 ret= Xorriso_fake_stbuf(xorriso, eff_source, &stbuf, &node, 4);
 if(ret<=0)
   return(0);
 if(!S_ISREG(stbuf.st_mode)) {
   Xorriso_msgs_submit(xorriso, 0, eff_dest, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text, "-paste_in: ISO file %s is not a data file",
           Text_shellsafe(eff_source, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   return(0);
 }
 
 /* >>> eventually obtain parameters from file name */;

 ret= Xorriso_restore(xorriso, 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
               bit3=report to info channel (else to result channel)
*/
int Xorriso_toc_line(struct XorrisO *xorriso, int flag)
{
 if(!(flag & 8)) {
   Xorriso_result(xorriso,0);
   return(1);
 }
 strcpy(xorriso->info_text, xorriso->result_line);
 Xorriso_info(xorriso, 0);
 return(1);
}


/* @param flag bit0= no output if no boot record was found
               bit1= short form
               bit3= report to info channel (else to result channel)
*/
int Xorriso_show_boot_info(struct XorrisO *xorriso, int flag)
{
 int ret, bin_path_valid= 0,has_isolinux_mbr= 0, i;
 unsigned int mbr_lba= 0;
 off_t lb0_count;
 char *respt, sfe[5*SfileadrL], path[SfileadrL];
 unsigned char lb0[2048];
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 IsoImage *image= NULL;
 ElToritoBootImage *bootimg;
 IsoFile *bootimg_node;
 IsoBoot *bootcat_node;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to print boot info", 0);
 if(ret<=0)
   return(0);
 respt= xorriso->result_line;
 image= isoburn_get_attached_image(drive);
 if(image == NULL) {
   ret= 0;
no_boot:;
   if(!(flag & 1)) {
     sprintf(respt, "Boot record  : none\n");
     Xorriso_toc_line(xorriso, flag & 8);
   }
   return(ret);
 }
 /* Important: no return before iso_image_unref(image); */

 /* Using the nodes with extreme care . They might be deleted meanwhile. */
 ret= iso_image_get_boot_image(image, &bootimg, &bootimg_node, &bootcat_node);
 iso_image_unref(image); /* release obtained reference */
 image= NULL;
 if(ret != 1)
   goto no_boot;
 ret= Xorriso_path_from_lba(xorriso, NULL, xorriso->loaded_boot_bin_lba,
                             path, 1);
 if(ret > 0)
   bin_path_valid= 1;
 sprintf(respt, "Boot record  : El Torito");
 if(bin_path_valid)
   ret= Xorriso_is_isohybrid(xorriso, bootimg_node, 0);
 else
   ret= 0;
 if(ret > 0) {
   /* Load and examine potential MBR */
   ret= burn_read_data(drive, (off_t) 0, (char *) lb0, (off_t) 2048,
                       &lb0_count, 2);
   if(ret > 0) {
     has_isolinux_mbr= 1;
     if(lb0[510] != 0x55 || lb0[511] != 0xaa)
       has_isolinux_mbr= 0;
     mbr_lba= lb0[432] | (lb0[433] << 8) | (lb0[434] << 16) | (lb0[435] << 24);
     mbr_lba/= 4;
     if(mbr_lba != xorriso->loaded_boot_bin_lba)
       has_isolinux_mbr= 0;
     if(has_isolinux_mbr) {
       for(i= 0; i < 426; i++)
         if(strncmp((char *) (lb0 + i), "isolinux", 8) == 0)
       break;
       if(i >= 426)
         has_isolinux_mbr= 0;
     }
     for(i= 462; i < 510; i++)
       if(lb0[i])
     break;
     if(i < 510)
       has_isolinux_mbr= 0;
   }
   if(has_isolinux_mbr)
     strcat(respt, " , ISOLINUX isohybrid MBR pointing to boot image");
   else
     strcat(respt, " , ISOLINUX boot image capable of isohybrid");
 }
 strcat(respt, "\n");
 Xorriso_toc_line(xorriso, flag & 8);
 if(flag & 2)
   return(1);
 if(bin_path_valid)
   sprintf(respt, "Boot bin_path: %s\n", Text_shellsafe(path, sfe, 0));
 else if(xorriso->loaded_boot_bin_lba <= 0)
   sprintf(respt, "Boot bin_path: -not-found-at-load-time-\n");
 else
   sprintf(respt, "Boot bin_path: -not-found-any-more-by-lba=%d\n",
           xorriso->loaded_boot_bin_lba);
 Xorriso_toc_line(xorriso, flag & 8);
 if(xorriso->loaded_boot_cat_path[0])
   sprintf(respt, "Boot cat_path: %s\n",
           Text_shellsafe(xorriso->loaded_boot_cat_path, sfe, 0));
 else
   sprintf(respt, "Boot cat_path: -not-found-at-load-time-\n");
 Xorriso_toc_line(xorriso, flag & 8);
 return(1);
} 


/* @param flag
               bit1=report about output drive
               bit3=report to info channel (else to result channel)
               bit4=do no report failure if no drive aquired
*/
int Xorriso_media_product(struct XorrisO *xorriso, int flag)
{
 int ret, profile_no;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 char *product_id= NULL, *media_code1= NULL, *media_code2= NULL;
 char *book_type= NULL, *manuf= NULL, profile_name[80], *respt;

 respt= xorriso->result_line;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to print media product info",
                                 flag & (2 | 16));
 if(ret <= 0)
   return(ret);
 ret= burn_disc_get_media_id(drive, &product_id, &media_code1, &media_code2,
                             &book_type, 0);
 if(ret > 0) {
   ret= burn_disc_get_profile(drive, &profile_no, profile_name);
   if(ret <= 0)
     return(ret);
   sprintf(respt, "Media product: %s , ", product_id);
   manuf= burn_guess_manufacturer(profile_no, media_code1, media_code2, 0);
   if(manuf != NULL) {
     if(strncmp(manuf, "Unknown ", 8) == 0)
       sprintf(respt + strlen(respt), "(not found in manufacturer list)\n");
     else
       sprintf(respt + strlen(respt), "%s\n", manuf);
   } else
     sprintf(respt + strlen(respt), "(error during manufacturer lookup)\n");
   free(product_id);
   free(media_code1);
   free(media_code2);
   if(book_type != NULL)
     free(book_type);
   if(manuf != NULL)
     free(manuf);
   Xorriso_toc_line(xorriso, flag & 8);
 }
 Xorriso_process_msg_queues(xorriso,0);

 return(1);
}


/* @param flag bit0=short report form
               bit1=report about output drive
               bit2=do not try to read ISO heads
               bit3=report to info channel (else to result channel)
               bit4=do no report failure if no drive aquired
               bit5=only report "Drive current" and "Drive type"
               bit6=report "Media product" with bit0
               bit7=only report "Drive current"
*/
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, track_size;
 int last_track_start= 0, last_track_size= -1, num_data= 0, is_data= 0;
 int is_inout_drive= 0, drive_role, status, num_formats, emul_lba;
 int num_payload= 0, num_wasted= 0, num_nondata= 0;
 char profile_name[80],*respt,*devadr, *typetext= "";
 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, size;
 unsigned dummy;
 struct isoburn_toc_disc *disc= NULL;
 struct isoburn_toc_session **sessions;
 struct isoburn_toc_track **tracks;
 int image_blocks= 0;
 char volume_id[33];
 struct burn_toc_entry next_toc_entry;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to print Table Of Content",
                                 flag & (2 | 16));
 if(ret<=0)
   {ret= 0; goto ex;}

 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_toc_line(xorriso, flag & 8);
 if(flag & 128)
   {ret= 1; goto ex;}
 sprintf(respt, "Drive type   : vendor '%s' product '%s' revision '%s'\n",
        dinfo[0].vendor, dinfo[0].product, dinfo[0].revision);
 if((flag & 32) | !(flag & 1))
   Xorriso_toc_line(xorriso, flag & 8);
 if(flag & 32)
   {ret= 1; goto ex;}
 
 ret= burn_disc_get_profile(drive, &profile_no, profile_name);
 s= isoburn_disc_get_status(drive);
 if(profile_no == 0x0002 && s == BURN_DISC_EMPTY)
   profile_no= 0;
 sprintf(respt, "Media current: ");
 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_toc_line(xorriso, flag & 8);

 if((flag & 64) || !(flag & 1)) {
   Xorriso_media_product(xorriso, flag & (2 | 8 | 16));
   if(xorriso->request_to_abort)
     {ret= 1; goto ex;}
 }

 sprintf(respt, "Media status : ");
 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_toc_line(xorriso, flag & 8);

 if(s == BURN_DISC_BLANK) {
   sprintf(respt, "Media summary: 0 sessions, 0 data blocks, 0 data");
   num_free= isoburn_disc_available_space(drive, NULL); 
   Sfile_scale((double) num_free, mem_text,5,1e4,1);
   sprintf(respt+strlen(respt), ", %s free\n", mem_text);
   Xorriso_toc_line(xorriso, flag & 8);
 }
 if(s != BURN_DISC_FULL && s != BURN_DISC_APPENDABLE)
   {ret= 1; goto ex;}
 if(xorriso->request_to_abort)
   {ret= 1; goto ex;}

 if(!(flag & 2))
   Xorriso_show_boot_info(xorriso, 1 | (flag & 8) | ((flag & 1) << 1));

 disc= isoburn_toc_drive_get_disc(drive);
 if(flag & 4)
   sprintf(respt, "TOC layout   : %3s , %9s , %10s\n",
           "Idx", "sbsector", "Size");
 else
   sprintf(respt, "TOC layout   : %3s , %9s , %10s , %s\n",
           "Idx", "sbsector", "Size", "Volume Id");
 if(!(flag&1))
   Xorriso_toc_line(xorriso, flag & 8);

 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)
       {ret= 0; goto ex;}
     sprintf(xorriso->info_text, "Cannot obtain Table Of Content");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0);
     {ret= 0; goto ex;}
   }

   /* fabricate TOC */
   typetext= "Other session";
   if(flag & 4) {
     ret= 0;
     typetext= "Session      ";
   } else
     ret= isoburn_read_iso_head(drive, 0, &image_blocks, volume_id, 1);
   if(ret>0) {
     sprintf(respt, "ISO session  : %3d , %9d , %9ds , %s\n",
             1, 0, image_blocks, volume_id);
     nwa= image_blocks;
   } else {
     nwa= 0;
     ret= burn_disc_get_formats(drive, &status, &size, &dummy,
                                &num_formats);
     if(ret>0 && status==BURN_FORMAT_IS_FORMATTED)
       nwa= size/2048;
     sprintf(respt, "%13s: %3d , %9d , %9ds , \n",
             typetext, 1, 0, nwa);
   } 
   if(!(flag&1))
     Xorriso_toc_line(xorriso, flag & 8);
   last_track_start= lba;
   num_payload= num_data= last_track_size= nwa;
   num_sessions= 1;
 } else {
   sessions= isoburn_toc_disc_get_sessions(disc, &num_sessions);
   for (session_no= 0; session_no<num_sessions && !(xorriso->request_to_abort);
        session_no++) {
     tracks= isoburn_toc_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;
       isoburn_toc_track_get_entry(tracks[track_no], &toc_entry);
       if (toc_entry.extensions_valid & 1) {
         /* DVD extension valid */
         lba= toc_entry.start_lba;
         track_size= toc_entry.track_blocks;
       } else {
         lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec,
                              toc_entry.pframe);
         if(track_no==num_tracks-1) {
           isoburn_toc_session_get_leadout_entry(sessions[session_no],
                                                 &next_toc_entry);
         } else {
           isoburn_toc_track_get_entry(tracks[track_no+1], &next_toc_entry);
         }
         track_size= burn_msf_to_lba(next_toc_entry.pmin, next_toc_entry.psec,
                                     next_toc_entry.pframe) - lba;
       }
       if(flag&(1|4))
         ret= 0;
       else {
         ret= isoburn_toc_track_get_emul(tracks[track_no], &emul_lba,
                                         &image_blocks, volume_id, 0);
         if(ret <= 0)
           ret= isoburn_read_iso_head(drive, lba, &image_blocks, volume_id, 1);
         if(image_blocks > track_size) {
           sprintf(xorriso->info_text,
              "Session %d bears ISO image size %ds larger than track size %ds",
              session_no + 1, image_blocks, track_size);
           Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING",
                               0);
           image_blocks= track_size;
         }
       }
       if(ret>0 && track_no==0) {
         sprintf(respt, "ISO session  : %3d , %9d , %9ds , %s\n",
                 session_no+1, lba, image_blocks , volume_id);
       } else if(ret>0) {
         sprintf(respt, "ISO track    : %3d , %9d , %9ds , %s\n",
                 track_count, lba, image_blocks , volume_id);
       } else if(track_no==0) {
         typetext= "Other session";
         if(flag & 4)
           typetext= "Session      ";
         sprintf(respt, "%13s: %3d , %9d , %9ds , \n",
                 typetext, session_no+1, lba, track_size);
       } else {
         typetext= "Other track  ";
         if(flag & 4)
           typetext= "Track        ";
         sprintf(respt, "%13s: %3d , %9d , %9ds , \n",
                 typetext, track_count, lba, track_size);
       } 
       if(!(flag&1))
         Xorriso_toc_line(xorriso, flag & 8);
       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;
     }
     isoburn_toc_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);
     }
     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)
   {ret= 1; goto ex;}
 num_wasted= lba - num_payload;
 num_nondata= lba - num_data;
 Sfile_scale(((double) num_data) * 2048.0, mem_text,5,1e4,1);

 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);

 sprintf(respt+strlen(respt), "\n");
 Xorriso_toc_line(xorriso, flag & 8);

 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_toc_line(xorriso, flag & 8);
   }
 }

 if (disc!=NULL)
   isoburn_toc_disc_free(disc);
 Xorriso_process_msg_queues(xorriso,0);
 ret= 1;
ex:;
 return(ret);
}


int Xorriso_show_devices(struct XorrisO *xorriso, int flag)
{
 char adr[BURN_DRIVE_ADR_LEN];
 int i, j, max_dev_len= 1, pad;
 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);

 sprintf(xorriso->info_text, "-----------------------------------------------------------------------------\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(strlen(adr)>max_dev_len)
     max_dev_len= strlen(adr);
 }
 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' ", i, adr);
   pad= max_dev_len-strlen(adr);
   if(pad>0)
     for(j= 0; j<pad; j++)
       strcat(respt, " ");
   sprintf(respt+strlen(respt), "%s :  '%-8.8s' '%s' \n",
           perms, drive_list[i].vendor, drive_list[i].product);
   Xorriso_result(xorriso,0);
 }
 sprintf(xorriso->info_text, "-----------------------------------------------------------------------------\n");
 Xorriso_info(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
               bit2= do not re-aquire drive
   @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];
 time_t start_time;
 char mode_names[4][80]= {"all", "fast", "deformat", "deformat_quickest"};

 ret= Xorriso_may_burn(xorriso, 0);
 if(ret <= 0)
   return(0);
 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));
 start_time= time(0);
 usleep(1000000);
 while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) {
   Xorriso_process_msg_queues(xorriso,0);
   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 in %d seconds )",
           percent, (int) (time(0) - start_time));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0);
   usleep(1000000);
 }
 Xorriso_process_msg_queues(xorriso,0);

 if(burn_drive_wrote_well(drive)) {
   sprintf(xorriso->info_text, "Blanking done\n");
   Xorriso_info(xorriso,0);
 } else {
   sprintf(xorriso->info_text, "Blanking failed.");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
 }
 if(!(flag & 4)) {
   ret= Xorriso_reaquire_outdev(xorriso, 
                  2 + (xorriso->in_drive_handle == xorriso->out_drive_handle));
   if(ret <= 0)
     return(-1);
 }
 return(1);
}


/* @param flag bit0= try to achieve faster formatting
               bit1= use parameter size (else use default size)
               bit2= do not re-aquire drive
               bit7= by_index mode:
                     bit8 to bit15 contain the index of the format to use.
   @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, off_t in_size, int flag)
{
 int ret, mode_flag= 0, index, status, num_formats;
 unsigned dummy;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 struct burn_progress p;
 double percent = 1.0;
 int current_profile;
 char current_profile_name[80];
 off_t size= 0;
 time_t start_time;
 enum burn_disc_status disc_state;

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

 if(flag & 2) {
   mode_flag= 0; /* format to given size */
 } else {
   mode_flag= 4; /* format to full size */
 }
 burn_disc_get_profile(drive, &current_profile, current_profile_name);

 if(flag&128) { /* by_index */
   index= (flag>>8) & 0xff;
   ret= burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats);
   if(ret<=0)
     num_formats= 0;
   if(ret<=0 || index<0 || index>=num_formats) {
     if(num_formats>0)
       sprintf(xorriso->info_text,
            "-format by_index_%d: format descriptors range from index 0 to %d",
            index, num_formats-1);
     else
       sprintf(xorriso->info_text,
               "-format by_index_%d: no format descriptors available", index);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     return(0);
   }
   mode_flag|= (flag & 0xff80);
   if(flag&1)
     mode_flag|= (1<<6);

 } else if(current_profile == 0x12) { /* DVD+RAM */
   if(!(flag & 2))
     mode_flag= 6; /* format to default payload size */
   if(flag&1)
     mode_flag|= (1<<6);

 } else if(current_profile == 0x13) { /* DVD-RW */
   if(flag&1) {
     sprintf(xorriso->info_text,
          "Detected formatted DVD-RW. Thus omitting desired fast format run.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     return(2);
   }

 } else if(current_profile == 0x14) { /* DVD-RW sequential */
   if(flag & 1) {
     size= 128*1024*1024;
     mode_flag= 1; /* format to size, then write size of zeros */
   } else
     mode_flag= 4;

 } else if(current_profile == 0x1a) { /* DVD+RW */
   if(flag&1) {
     sprintf(xorriso->info_text,
             "Detected DVD+RW. Thus omitting desired fast format run.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     return(2);
   }

 } else if(current_profile == 0x41) { /* BD-R SRM */
   if(!(flag & 2))
     mode_flag= 6; /* format to default payload size */
   if(flag&1)
     mode_flag|= (1<<6);

 } else if(current_profile == 0x43) { /* BD-RE */
   if(!(flag & 2))
     mode_flag= 6; /* format to default payload size */
   if(flag&1)
     mode_flag|= (1<<6);

 } else {
   sprintf(xorriso->info_text,
          "-format: Unsuitable media detected.");
   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(!(flag & 1))
   mode_flag|= 16; /* enable re-formatting */

 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);
 if(flag & 2)
   size= in_size;
 burn_disc_format(drive, size, mode_flag);

 start_time= time(0);
 usleep(1000000);
 while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) {
   Xorriso_process_msg_queues(xorriso,0);
   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, "Formatting  ( %.1f%% done in %d seconds )",
           percent, (int) (time(0) - start_time));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "UPDATE", 0);
   usleep(1000000);
 }

 Xorriso_process_msg_queues(xorriso,0);
 
 if(burn_drive_wrote_well(drive)) {
   sprintf(xorriso->info_text, "Formatting done\n");
   Xorriso_info(xorriso,0);
 } else {
   sprintf(xorriso->info_text,
           "libburn indicates failure with formatting."); 
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(-1);
 }

 if(!(flag & 4)) {
   ret= Xorriso_reaquire_outdev(xorriso, 
                  2 + (xorriso->in_drive_handle == xorriso->out_drive_handle));
   if(ret <= 0)
     return(-1);
 }
 disc_state = isoburn_disc_get_status(drive);
 if(disc_state==BURN_DISC_FULL && !(flag&1)) {
   /* Blank because full format certification pattern might be non-zero */
   ret= Xorriso_blank_media(xorriso, 1);
   if(ret <= 0)
     return(0);
 }
 return(1);
}


/* @param flag bit2= formatting rather than blanking
   @return 0=failure, did not touch media , -1=failure, altered media
           1=success, altered media       ,  2=success, did not touch media
*/
int Xorriso_blank_as_needed(struct XorrisO *xorriso, int flag)
{
 int ret, is_formatted= -1, status, num_formats, did_work= 0;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 enum burn_disc_status disc_state;
 unsigned dummy;
 int current_profile;
 char current_profile_name[80];
 off_t size;

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

 burn_disc_get_profile(drive, &current_profile, current_profile_name);

 ret= burn_disc_get_formats(drive, &status, &size, &dummy, &num_formats);
 if(ret>0) {
   if(status==BURN_FORMAT_IS_FORMATTED)
     is_formatted= 1;
   else if(status == BURN_FORMAT_IS_UNFORMATTED)
     is_formatted= 0;
 }
 if(current_profile == 0x12 || current_profile == 0x43) { /* DVD+RAM , BD-RE */
   if(is_formatted<0) {
     sprintf(xorriso->info_text,
             "-blank or -format: Unclear formatting status of %s",
             current_profile_name);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     return(0);
   }
   if(!is_formatted) {
     ret= Xorriso_format_media(xorriso, (off_t) 0, (current_profile == 0x43));
     if(ret <= 0)
       return(ret);
     did_work= (ret == 1);
   }
 } else if(current_profile == 0x14 && (flag&4)) { /* DVD-RW sequential */
   ret= Xorriso_format_media(xorriso, (off_t) 0, 0);
   if(ret <= 0)
     return(ret);
   did_work= (ret == 1);
 } else if(current_profile == 0x41) { /* BD-R SRM */
   if(!is_formatted) {
     ret= Xorriso_format_media(xorriso, (off_t) 0, 1);
     if(ret <= 0)
       return(ret);
     did_work= (ret == 1);
   }
 }

 disc_state = isoburn_disc_get_status(drive);
 if(disc_state != BURN_DISC_BLANK && !(flag&4)) {
   ret= Xorriso_blank_media(xorriso, 1);
   return(ret);
 }
 if(did_work)
   return(1);
 sprintf(xorriso->info_text, "%s as_needed: no need for action detected",
         (flag&4) ? "-format" : "-blank");
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
 return(2);
}


/* @return <=0 error, 1 success
*/
int Xorriso_list_formats(struct XorrisO *xorriso, int flag)
{
 int ret, i, status, num_formats, profile_no, type;
 off_t size;
 unsigned dummy;
 char status_text[80], profile_name[90], *respt;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;

 respt= xorriso->result_line;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                         "on attempt to obtain format descriptor list", 1 | 2);
 if(ret<=0)
   return(0);
 if(ret == 2)
   goto ex;
 ret = burn_disc_get_formats(drive, &status, &size, &dummy,
                             &num_formats);
 if(ret<=0) {
   sprintf(xorriso->info_text, "Cannot obtain format list info");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }
 ret= Xorriso_toc(xorriso, 3);
 if(ret<=0)
   goto ex;
 ret= burn_disc_get_profile(drive, &profile_no, profile_name);
 if(ret<=0)
   goto ex;

 if(status == BURN_FORMAT_IS_UNFORMATTED)
   sprintf(status_text, "unformatted, up to %.1f MiB",
                        ((double) size) / 1024.0 / 1024.0);
 else if(status == BURN_FORMAT_IS_FORMATTED) {
   if(profile_no==0x12 || profile_no==0x13 || profile_no==0x1a ||
      profile_no==0x43)
     sprintf(status_text, "formatted, with %.1f MiB",
                         ((double) size) / 1024.0 / 1024.0);
   else
     sprintf(status_text, "written, with %.1f MiB",
                         ((double) size) / 1024.0 / 1024.0);
 } else if(status == BURN_FORMAT_IS_UNKNOWN) {
   if (profile_no > 0)
     sprintf(status_text, "intermediate or unknown");
   else
     sprintf(status_text, "no media or unknown media");
 } else
   sprintf(status_text, "illegal status according to MMC-5");
 sprintf(respt, "Format status: %s\n", status_text);
 Xorriso_result(xorriso,0);

 for (i= 0; i < num_formats; i++) {
   ret= burn_disc_get_format_descr(drive, i, &type, &size, &dummy);
   if (ret <= 0)
 continue;
   sprintf(respt, "Format idx %-2d: %2.2Xh , %.fs , %.1f MiB\n",
          i, type, ((double) size) / 2048.0, ((double) size) / 1024.0/1024.0);
   Xorriso_result(xorriso,0);
 }
 ret= 1;
ex:;
 return(ret);
}


/* @param flag bit0= cdrecord style
               bit1= obtain outdrive, else indrive
   @return <=0 error, 1 success
*/
int Xorriso_list_profiles(struct XorrisO *xorriso, int flag)
{
 int ret, i;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 int num_profiles, profiles[64];
 char is_current[64], profile_name[90], *respt;

 respt= xorriso->result_line;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                          "on attempt to obtain profile list", 1 | (flag & 2));
 if(ret<=0)
   return(0);
 burn_drive_get_all_profiles(drive, &num_profiles, profiles, is_current);
 for(i= 0; i < num_profiles; i++) {
   ret= burn_obtain_profile_name(profiles[i], profile_name);
   if(ret <= 0)
     strcpy(profile_name, "unknown");
   sprintf(respt, "%s 0x%4.4X (%s)%s\n",
           flag & 1 ? "Profile:" : "Profile      :",
           (unsigned int) profiles[i],
           profile_name, is_current[i] ? " (current)" : "");
   Xorriso_result(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)
{
 int ret;
 int lba1= 0, lba2= 0;

 ret= Xorriso__file_start_lba(*((IsoNode **) node1), &lba1, 0);
 if(ret!=1) 
   lba1= 0;
 ret= Xorriso__file_start_lba(*((IsoNode **) node2), &lba2, 0);
 if(ret!=1)
   lba2= 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
               bit6= do not delete eventually existing node from di_array
   @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= 0, node_idx;

 /* 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;}
 }

#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 | 64)) | 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;
   }
 }

 if(!(flag & 64))
   Xorriso_invalidate_di_item(xorriso, victim_node, 0);

#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_set_change_pending(xorriso, 0);
 ret= 1+!!is_dir;
ex:;
 if(sfe!=NULL)
   free(sfe);
 if(sub_path!=NULL)
   free(sub_path);
 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;
 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;}
 }

 *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:;
 if(sfe!=NULL)
   free(sfe);
 if(path!=NULL)
   free(path);
 if(show_path!=NULL)
   free(show_path);
 if(iter!=NULL)
   iso_dir_iter_free(iter);
 if(node_array!=NULL)
   free((char *) node_array);
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


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;
 char *a_text= NULL, *d_text= 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) | 16);
   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 */
     iso_node_get_acl_text(node, &a_text, &d_text, 16);
     ret= Xorriso_format_ls_l(xorriso, &stbuf,
                              1 | ((a_text != NULL || d_text != NULL) << 1));
     iso_node_get_acl_text(node, &a_text, &d_text, 1 << 15);
     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, 1);
     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;
 char *adr= NULL;

 adr= malloc(SfileadrL);
 if(adr==NULL) {
   Xorriso_no_malloc_memory(xorriso, &adr, 0);
   {ret= -1; goto ex;}
 }

 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:;
 if(adr!=NULL)
   free(adr);
 if(flag&2)
   (*dive_count)--;
 if(iter != NULL)
   iso_dir_iter_free(iter);
 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",
         (unsigned int) (mode & 0xffff), Text_shellsafe(path, sfe, 0));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 Xorriso_set_change_pending(xorriso, 0);
 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_set_change_pending(xorriso, 0);
 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_set_change_pending(xorriso, 0);
 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_set_change_pending(xorriso, 0);
 Xorriso_process_msg_queues(xorriso,0);
 return(1);
}


int Xorriso__start_end_lbas(IsoNode *node,
                            int *lba_count, int **start_lbas, int **end_lbas,
                            off_t *size, int flag)
{
 int section_count= 0, ret, i;
 struct iso_file_section *sections= NULL;

 *lba_count= 0;
 *start_lbas= *end_lbas= NULL;
 *size= 0;
 if(!LIBISO_ISREG(node))
   return(0);
 *size= iso_file_get_size((IsoFile *) node);
 ret= iso_file_get_old_image_sections((IsoFile *) node, &section_count,
                                      &sections, 0);
 if(ret < 0)
   {ret= -1; goto ex;}
 if(ret != 1 || section_count <= 0)
   {ret= 0; goto ex;}
 *start_lbas= calloc(section_count, sizeof(int));
 *end_lbas= calloc(section_count, sizeof(int));
 if(*start_lbas == NULL || *end_lbas == NULL)
   {ret= -1; goto ex;}
 for(i= 0; i < section_count; i++) {
   (*start_lbas)[i]= sections[i].block;
   (*end_lbas)[i]= sections[i].block + sections[i].size / 2048 - 1;
   if(sections[i].size % 2048)
     (*end_lbas)[i]++;
 }
 *lba_count= section_count;
 ret= 1;
ex:;
 if(sections != NULL)
   free((char *) sections);
 if(ret <= 0) {
   if((*start_lbas) != NULL)
     free((char *) *start_lbas);
   if((*end_lbas) != NULL)
     free((char *) *end_lbas);
   *start_lbas= *end_lbas= NULL;
   *lba_count= 0;
 }
 return(ret);
}


int Xorriso__file_start_lba(IsoNode *node,
                           int *lba, int flag)
{
 int *start_lbas= NULL, *end_lbas= NULL, lba_count= 0, i, ret;
 off_t size;

 *lba= -1;
 ret= Xorriso__start_end_lbas(node, &lba_count, &start_lbas, &end_lbas,
                              &size, 0);
 if(ret <= 0)
   return(ret);
 for(i= 0; i < lba_count; i++) {
   if(*lba < 0 || start_lbas[i] < *lba)
     *lba= start_lbas[i];
 }
 if(start_lbas != NULL)
   free((char *) start_lbas);
 if(end_lbas != NULL)
   free((char *) end_lbas);
 if(*lba < 0)
   return(0);
 return(1);
}


/* @param damage_start Returns first damaged byte address
   @param damage_end   Returns first byte address after last damaged byte
   @return <0 error, 0=undamaged , 1=damaged
*/
int Xorriso_file_eval_damage(struct XorrisO *xorriso, IsoNode *node,
                             off_t *damage_start, off_t *damage_end,
                             int flag)
{
 int *start_lbas= NULL, *end_lbas= NULL, lba_count= 0, sect;
 int i, sectors, sector_size, ret;
 off_t sect_base= 0, size= 0, byte;
 struct SectorbitmaP *map;

 *damage_start= *damage_end= -1;
 map= xorriso->in_sector_map;
 if(map == NULL)
   return(0);
 Sectorbitmap_get_layout(map, &sectors, &sector_size, 0);
 sector_size/= 2048;

 ret= Xorriso__start_end_lbas(node, &lba_count, &start_lbas, &end_lbas,
                              &size, 0);
 if(ret <= 0) {
   Xorriso_process_msg_queues(xorriso, 0);
   return(ret);
 }
 for(sect= 0; sect < lba_count; sect++) {
   for(i= start_lbas[sect]; i <= end_lbas[sect]; i+= sector_size) {
     if(Sectorbitmap_is_set(map, i / sector_size, 0) == 0) {
       byte= ((off_t) 2048) * ((off_t) (i - start_lbas[sect])) + sect_base;
       if(*damage_start < 0 || byte < *damage_start)
         *damage_start= byte;
       if(byte + (off_t) 2048 > *damage_end)
         *damage_end= byte + (off_t) 2048;
     }
   }
   sect_base+= ((off_t) 2048) *
               ((off_t) (end_lbas[sect] - start_lbas[sect] + 1));
 }
 if(*damage_end > size)
   *damage_end= size;
 if(start_lbas != NULL)
   free((char *) start_lbas);
 if(end_lbas != NULL)
   free((char *) end_lbas);
 if(*damage_start < 0)
   return(0);
 return(1);
}


int Xorriso_report_lba(struct XorrisO *xorriso, char *show_path,
                       IsoNode *node, int flag)
{
 int ret, *start_lbas= NULL, *end_lbas= NULL, lba_count, i;
 off_t size;
 char sfe[5*SfileadrL];

 ret= Xorriso__start_end_lbas(node, &lba_count, &start_lbas, &end_lbas,
                              &size, 0);
 if(ret < 0) {
   Xorriso_process_msg_queues(xorriso, 0);
   {ret= -1; goto ex;}
 }
 if(ret == 0)
   {ret= 1; goto ex;} /* it is ok to ignore other types */
 for(i= 0; i < lba_count; i++) {
   sprintf(xorriso->result_line,
           "File data lba: %2d , %8d , %8d , %8.f , %s\n",
           i, start_lbas[i], end_lbas[i] + 1 - start_lbas[i], (double) size,
           Text_shellsafe(show_path, sfe, 0));
   Xorriso_result(xorriso, 0);
 }
 ret= 1;
ex:;
 if(start_lbas != NULL)
   free((char *) start_lbas);
 if(end_lbas != NULL)
   free((char *) end_lbas);
 return(ret);
}


int Xorriso_report_damage(struct XorrisO *xorriso, char *show_path,
                          IsoNode *node, int flag)
{
 int ret;
 off_t size= 0, damage_start, damage_end;
 char sfe[5*SfileadrL];

 ret= Xorriso_file_eval_damage(xorriso, node, &damage_start, &damage_end, 0);
 if(ret < 0)
   return(0);

 if(LIBISO_ISREG(node))
   size= iso_file_get_size((IsoFile *) node);
 if(ret > 0) {
   sprintf(xorriso->result_line, "File damaged : %8.f , %8.f , %8.f , %s\n",
           (double) damage_start, (double) (damage_end - damage_start) ,
           (double) size, Text_shellsafe(show_path, sfe, 0));
   Xorriso_result(xorriso, 0);
 } else {
   sprintf(xorriso->result_line, "File seems ok: %8.f , %8.f , %8.f , %s\n",
           -1.0, -1.0, (double) size, Text_shellsafe(show_path, sfe, 0));
   Xorriso_result(xorriso, 0);
 }
 return(1);
}


/* @return  see Xorriso_update_interpreter()
*/
int Xorriso_widen_hardlink(struct XorrisO *xorriso, void * boss_iter,
                           IsoNode *node,
                           char *abs_path, char *iso_prefix, char *disk_prefix,
                           int flag)
{
 int ret= 0, idx, low, high, i, do_widen= 0, compare_result= 0;
 char disk_path[SfileadrL];

 /* Lookup all di_array instances of node */
 if(LIBISO_ISDIR(node))
   return(3);
 ret= Xorriso_search_di_range(xorriso, node, &idx, &low, &high, 2);
 if(ret <= 0)
   return(3);
 /* Check and reset di_do_widen bits */
 for(i= low; i <= high; i++) {
   if(node != xorriso->di_array[i]) /* might be NULL */
 continue;
   if(xorriso->di_do_widen[i / 8] & (1 << (i % 8)))
     do_widen= 1;
   xorriso->di_do_widen[i / 8]&= ~(1 << (i % 8));
 }
 if(idx < 0 || !do_widen)
   return(3);

 ret= Xorriso_pfx_disk_path(xorriso, abs_path, iso_prefix, disk_prefix,
                            disk_path, 0);
 if(ret <= 0)
   return(ret);
 ret= Sfile_type(disk_path, 1);
 if(ret < 0)
   return(3); /* does not exist on disk */

 /* >>> compare_result bit17 = is_split */;

 ret= Xorriso_update_interpreter(xorriso, boss_iter, compare_result, disk_path,
                                 abs_path, 1);
 if(ret <= 0)
   return(ret);
 return(ret);
}


/* @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, *text_2, sfe[5*SfileadrL], *iso_prefix, md5[16];
 struct FindjoB *subjob;
 struct stat dir_stbuf;

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

 hflag= 16*!(flag&2);
 ret= 1;
 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 if(action == 21) { /* report_damage */
   ret= Xorriso_report_damage(xorriso, show_path, node, 0);
 } else if(action == 22) {
   ret= Xorriso_report_lba(xorriso, show_path, node, 0);
 } else if(action == 23) { /* internal: memorize path of last matching node */
   ret= Findjob_set_found_path(job, show_path, 0);
 } else if(action == 24) {
   ret= Xorriso_getfacl(xorriso, (void *) node, show_path, NULL, 0);
 } else if(action == 25) {
   if(target == NULL || target[0] || text_2 == NULL || text_2[0])
     ret= Xorriso_setfacl(xorriso, (void *) node, show_path, target, text_2,0);
 } else if(action == 26) {
   ret= Xorriso_getfattr(xorriso, (void *) node, show_path, NULL, 0);
 } else if(action == 27) {
   ret= Xorriso_path_setfattr(xorriso, (void *) node, show_path,
                              target, strlen(text_2), text_2, 0);
 } else if(action == 28) { /* set_filter */
   ret= Xorriso_set_filter(xorriso, (void *) node, show_path, target, 1 | 2);
 } else if(action == 29) { /* show_stream */
   ret= Xorriso_show_stream(xorriso, (void *) node, show_path, 1 | 2);
 } else if(action == 30) { /* internal: count */
   xorriso->node_counter++;
 } else if(action == 31) { /* internal: register */
   if(xorriso->node_counter < xorriso->node_array_size) {
     xorriso->node_array[xorriso->node_counter++]= (void *) node;
     iso_node_ref(node); /* In case node gets deleted from tree during
                            the lifetime of xorriso->node_array */
   }
 } else if(action == 32) { /* internal: widen_hardlinks disk_equiv */
   Findjob_get_start_path(job, &iso_prefix, 0);
   ret= Xorriso_widen_hardlink(xorriso, (void *) boss_iter, node, abs_path,
                               iso_prefix, target, 0);
   if(ret==2)
     deleted= 1;
 } else if(action == 33) { /* get_any_xattr */
   ret= Xorriso_getfattr(xorriso, (void *) node, show_path, NULL, 8);
 } else if(action == 34) { /* get_md5 */
   ret= Xorriso_get_md5(xorriso, (void *) node, show_path, md5, 0);
   if(ret >= 0)
     ret= 1;
 } else if(action == 35) { /* check_md5 */
   ret= Xorriso_check_md5(xorriso, (void *) node, show_path, 2);
   if(ret == 0)
     xorriso->find_check_md5_result|= 1;
   else if(ret < 0)
     xorriso->find_check_md5_result|= 2;
   else if(ret == 1)
     xorriso->find_check_md5_result|= 8;
   else if(ret == 2)
     xorriso->find_check_md5_result|= 4;
   if(ret >= 0)
     ret= 1;
 } else if(action == 36) { /* make_md5 */
   ret = Xorriso_make_md5(xorriso, (void *) node, show_path, 0);
   if(ret >= 0)
     ret= 1;
 } 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);
}


int Exprtest_match(struct XorrisO *xorriso, struct ExprtesT *ftest,
                   void *node_pt, char *name, char *path,
                   struct stat *boss_stbuf, struct stat *stbuf, int flag)
/*
return:
 <0 = error
  0 = does not match
  1 = does match
  2 = immediate decision : does not match
  3 = immediate decision : does match
*/
{
 int value=0, ret, start_lba, end_lba;
 int lba_count, *file_end_lbas= NULL, *file_start_lbas= NULL, i;
 void *arg1, *arg2;
 char ft, *decision, md5[16];
 regmatch_t name_match;
 off_t damage_start, damage_end, size;
 void *xinfo_dummy;
 IsoNode *node;
 IsoStream *stream;

 if(ftest == NULL)
   return(1);

 node= (IsoNode *) node_pt;
 arg1= ftest->arg1;
 arg2= ftest->arg2;

 if(node == NULL) {
   if(ftest->test_type > 2 && ftest->test_type != 4) {
     value= 0;
     goto ex;
   }
 }

 switch(ftest->test_type) {
 case 0: /* -false */
   value= 0;

 break; case 1: /* -name *arg1 (regex in *arg2) */
   ret= regexec(arg2, name, 1, &name_match, 0);
   value= !ret;

 break; case 2: /* -type *arg1 */
   value= 1;
   ft= *((char *) arg1);
   if(ft!=0) {
     if(S_ISBLK(stbuf->st_mode)) {
       if(ft!='b')
         value= 0;
     } else if(S_ISCHR(stbuf->st_mode)) {
       if(ft!='c')
         value= 0;
     } else if(S_ISDIR(stbuf->st_mode)) {
       if(ft=='m') {
         if(node != NULL)
           value= 0;
         else if(boss_stbuf==NULL)
           value= 0;
         else if(boss_stbuf->st_dev == stbuf->st_dev)
           value= 0;
       } else if(ft!='d')
         value= 0;
     } else if(S_ISFIFO(stbuf->st_mode)) {
       if(ft!='p')
         value= 0;
     } else if(S_ISREG(stbuf->st_mode)) {
       if(ft!='f' && ft!='-')
         value= 0;
     } else if(((stbuf->st_mode)&S_IFMT)==S_IFLNK) {
       if(ft!='l')
         value= 0;
     } else if(((stbuf->st_mode)&S_IFMT)==S_IFSOCK) {
       if(ft!='s')
         value= 0;
     } else if((flag & 1) && ((stbuf->st_mode) & S_IFMT) == Xorriso_IFBOOT) {
       if(ft!='e' || node == NULL)
         value= 0;
     } else {
       if(ft!='X')
         value= 0;
     }
   }

 break; case 3: /* -damaged */;
   value= Xorriso_file_eval_damage(xorriso, node, &damage_start, &damage_end,
                                   0);
   if(value > 0)
     value= 1;

 break; case 4: /* -lba_range *arg1 *arg2 */
   value= 1;
   start_lba= *((int *) ftest->arg1);
   end_lba= *((int *) ftest->arg2);
   if(node == NULL) {
     value= !(start_lba >= 0);
     goto ex;
   }
   ret= Xorriso__start_end_lbas(node, &lba_count,
                                &file_start_lbas, &file_end_lbas, &size, 0);
   if(ret <= 0) {
     if(ret < 0)
       Xorriso_process_msg_queues(xorriso, 0);
     if(start_lba >= 0)
       value= 0;
   } else {
     for(i= 0; i < lba_count; i++) {
       if(start_lba >= 0) {
         if(file_end_lbas[i] < start_lba || file_start_lbas[i] > end_lba)
           value= 0;
       } else {
         if(file_end_lbas[i] >= -start_lba && file_start_lbas[i] <= -end_lba)
           value= 0;
       }
     }
   }

 break; case 5: /* -has_acl */
   ret = Xorriso_getfacl(xorriso, (void *) node, "", NULL, 2);
   if(ret <= 0) {
     value= -1;
     Xorriso_process_msg_queues(xorriso, 0);
     goto ex;
   }
   value= (ret == 1);

 break; case 6: /* -has_xattr */
        case 14: /* -has_any_xattr */
   ret = Xorriso_getfattr(xorriso, (void *) node, "", NULL,
                          64 | (8 * (ftest->test_type == 14)));
   if(ret < 0) {
     value= -1;
     Xorriso_process_msg_queues(xorriso, 0);
     goto ex;
   }
   value= (ret > 0);

 break; case 7: /* -has_aaip */
   ret= iso_node_get_xinfo(node, aaip_xinfo_func, &xinfo_dummy);
   if(ret < 0) {
     value= -1;
     Xorriso_process_msg_queues(xorriso, 0);
     goto ex;
   }
   value= (ret > 0);

 break; case 8: /* -has_filter */
   value= 0;
   if(LIBISO_ISREG(node)) {
     stream= iso_file_get_stream((IsoFile *) node);
     if(iso_stream_get_input_stream(stream, 0) != NULL)
       value= 1;
   }

 break; case 9: /* -wanted_node arg1 (for internal use) */
   value= (((IsoNode *) arg1) == node);

 break; case 10: /* -pending_data */
   value= 1;
   if(!LIBISO_ISREG(node)) {
     value= 0;
   } else {
     ret= Xorriso__file_start_lba(node, &start_lba, 0);
     if(ret > 0 && start_lba >= 0)
       value= 0;
   }

 break; case 11: /* -decision */
   value= 2;
   decision= (char *) arg1;
   if(strcmp(decision, "yes") == 0 || strcmp(decision, "true") == 0)
     value= 3;

 break; case 12: /* -prune */
   value= 1;
   ftest->boss->prune= 1;

 break; case 13: /* -wholename *arg1 (regex in *arg2) */
   ret= regexec(arg2, path, 1, &name_match, 0);
   value= !ret;

 break; case 15: /* -has_md5 */
   ret= Xorriso_get_md5(xorriso, node, path, md5, 1);
   value= (ret > 0);

 break; default:

   /* >>> complain about unknown test type */;

   value= -1;

 }

ex:;
 if(ftest->invert && value<=1 && value>=0)
   value= !value;
 if(file_start_lbas != NULL)
   free((char *) file_start_lbas);
 if(file_end_lbas != NULL)
   free((char *) file_end_lbas);
 return(value);
}


/* @return <0 = error , 0 = no match , 1 = match */
int Xorriso_findi_test(struct XorrisO *xorriso, struct FindjoB *job,
                       IsoNode *node, char *name, char *path,
                       struct stat *boss_stbuf, struct stat *stbuf,
                       int depth, int flag)
{
 int ret;

 job->prune= 0;
 ret= Findjob_test_2(xorriso, job, node, name, path, boss_stbuf, stbuf, 1);
 if(ret <= 0)
   return(ret);
 return(1);
}


/* @param flag bit0= recursion
               bit1= do not count deleted files with rm and rm_r
               bit2= do not dive into split file directories
                     (implicitly given with actions 14=compare and 17=update)
   @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= NULL;
 struct stat stbuf;
 char *name;
 off_t mem;
 IsoNode **node_array= NULL;
 int node_count, node_idx;
 char *path= NULL, *abs_path= NULL;

 if(xorriso->request_to_abort)
   {ret= 0; goto ex;}

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

 action= Findjob_get_action(job, 0);
 if(action<0)
   action= 0;
 if(!(flag & 1)) {
   if(action == 21) {
     sprintf(xorriso->result_line, "Report layout: %8s , %8s , %8s , %s\n",
             "at byte", "Range", "Filesize", "ISO image path");
     Xorriso_result(xorriso, 0);
   } else if(action == 22) {
     sprintf(xorriso->result_line,
             "Report layout: %2s , %8s , %8s , %8s , %s\n",
             "xt", "Startlba", "Blocks", "Filesize", "ISO image path");
     Xorriso_result(xorriso, 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);
   if(ret<=0)
     goto ex;

   name= strrchr(dir_path, '/');
   if(name==NULL)
     name= dir_path;
   else
     name++;

   ret= Xorriso_findi_test(xorriso, job, iso_node, name, path, NULL, dir_stbuf,
                           depth, 0);
   if(ret<0)
     goto ex;
   if(job->prune)
     no_dive= 1;
   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(xorriso->request_to_abort)
       {ret= 0; goto ex;}
     if(ret==2) {
       /* re-determine dir_node in case it has a new persona */ 
       ret= Xorriso_node_from_path(xorriso, volume, path, &iso_node, 1);
       if(ret==0) {
         deleted= 1;
         {ret= 2; goto ex;}
       }
       if(ret<0)
         {ret= 0; goto ex;}
       dir_node= (IsoDir *) iso_node;
       ret= Xorriso_fake_stbuf(xorriso, "", dir_stbuf, &iso_node, 1);
       if(ret<=0)
         goto ex;
     }
     if(ret==3)
       no_dive= 1;
   }
 }
 if(no_dive || !LIBISO_ISDIR((IsoNode *) dir_node))
   {ret= 1; goto ex;}
 if(action == 14 || action == 17 || (flag & 4))
   if(Xorriso_is_split(xorriso, dir_path, (IsoNode *) dir_node, 1)>0)
     {ret= 1; goto ex;}

 mem= boss_mem;
 hflag= 1;
 if(action==1 || action==2 || action==3 || action==17 || action == 28 ||
    action == 32)
   hflag|= 2; /* need freedom to manipulate image */
 if(action==14 || action==17 || action == 28 || action == 35 || action == 36)
   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)
 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= Xorriso_findi_test(xorriso, job, node, name, path, dir_stbuf, &stbuf,
                           depth, 0);
   if(ret<0)
     goto ex;
   if(job->prune)
     no_dive= 1;
   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(xorriso->request_to_abort)
       {ret= 0; goto ex;}
     if(ret==2) { /* node has been deleted */
       /* re-determine node in case it has a new persona */ 
       if(volume==NULL) {
         ret= Xorriso_get_volume(xorriso, &volume, 0);
         if(ret<=0)
           {ret= -1; goto ex;}
       }
       ret= Xorriso_node_from_path(xorriso, volume, abs_path, &node, 1);
       if(ret==0)
 continue;
       if(ret<0)
         {ret= 0; goto ex;}
       ret= Xorriso_fake_stbuf(xorriso, "", &stbuf, &node, 1);
       if(ret<0)
         goto ex;
       if(ret==0)
 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:;
 if(path!=NULL)
   free(path);
 if(abs_path!=NULL)
   free(abs_path);
 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);
}


int Xorriso_node_is_valid(struct XorrisO *xorriso, IsoNode *in_node, int flag)
{
 IsoNode *node, *parent;

 for(node= in_node; 1; node= parent) {
   parent= (IsoNode *) iso_node_get_parent(node);
   if(parent == node)
 break;
   if(parent == NULL)
     return(0); /* Node is not in the tree (any more) */
 }
 return(1);
}


int Xorriso_path_from_node(struct XorrisO *xorriso, IsoNode *in_node,
                           char path[SfileadrL], int flag)
{
 int ret, i, comp_count= 0;
 IsoNode *node, *parent, **components= NULL;
 char *wpt, *npt;

 for(node= in_node; 1; node= parent) {
   parent= (IsoNode *) iso_node_get_parent(node);
   if(parent == node)
 break;
   if(parent == NULL)
     return(0); /* Node is not in the tree (any more) */
   comp_count++;
 }
 if(comp_count == 0) {
   strcpy(path, "/");
   return(1);
 }
 components= calloc(comp_count, sizeof(IsoNode *));
 if(components == NULL) {
   Xorriso_no_malloc_memory(xorriso, NULL, 0);
   ret= -1; goto ex;
 }
 i= comp_count;
 for(node= in_node; 1; node= parent) {
   parent= (IsoNode *) iso_node_get_parent(node);
   if(parent == node)
 break;
   components[--i]= node;
 }

 wpt= path;
 for(i= 0; i < comp_count; i++) {
   npt= (char *) iso_node_get_name(components[i]);
   if((wpt - path) + strlen(npt) + 1 >= SfileadrL) {

     /* >>> path is getting much too long */;

     ret= -1; goto ex;
   }
   *(wpt++)= '/';
   strcpy(wpt, npt);
   wpt+= strlen(npt);
   *wpt= 0;
 }
 ret= 1;
ex:;
 if(components != NULL)
   free(components);
 return(ret);
}


/* <<< The lookup from node pointer will be done by Xorriso_path_from_node()
       (Currently it runs a full tree traversal)
       Parameter node and flag bit0 will vanish then
*/
/* @param flag bit0= use lba rather than node pointer
*/
int Xorriso_path_from_lba(struct XorrisO *xorriso, IsoNode *node, int lba,
                          char path[SfileadrL], int flag)
{
 int ret;
 struct FindjoB *job= NULL;
 struct stat dir_stbuf;
 char *found_path;

 path[0]= 0;
 if((flag & 1) && lba <= 0)
   return(0);

 ret= Findjob_new(&job, "/", 0);
 if(ret <= 0) {
   Xorriso_no_findjob(xorriso, "path_from_node", 0);
   return(ret);
 }
 if(flag & 1)
   Findjob_set_lba_range(job, lba, 1, 0);
 else
   Findjob_set_wanted_node(job, (void *) node, 0);
 Findjob_set_action_found_path(job, 0);
 ret= Xorriso_findi(xorriso, job, NULL,  (off_t) 0,
                    NULL, "/", &dir_stbuf, 0, 0);
 if(ret > 0) {
   ret= 1;
   Findjob_get_found_path(job, &found_path, 0);
   if(found_path == NULL)
     ret= 0;
   else if(Sfile_str(path, found_path, 0) <= 0)
     ret= -1;
 }
 Findjob_destroy(&job, 0);
 return(ret);
}


/* @param flag bit0= do not dive into trees
               bit1= do not perform job->action on resulting node array
               bit2= do not free node_array after all actions are done
*/
int Xorriso_findi_sorted(struct XorrisO *xorriso, struct FindjoB *job,
                         off_t boss_mem, int filec, char **filev, int flag)
{
 int i, ret, find_flag= 0;
 struct FindjoB array_job, *proxy_job= NULL, *hindmost= NULL, *hmboss= NULL;
 struct stat dir_stbuf;
 IsoNode *node;
 char abs_path[SfileadrL];
 off_t mem_needed= 0;

 array_job.start_path= NULL;

 if(job->action == 14 || job->action == 17)
   find_flag|= 4;
 if(job->action>=9 && job->action<=13) { /* actions which have own findjobs */
   /* array_job replaces the hindmost job in the chain */
   for(hindmost= job; hindmost->subjob != NULL; hindmost= hindmost->subjob)
     hmboss= hindmost;
   if(hmboss == NULL)
     {ret= -1; goto ex;}
   memcpy(&array_job, hindmost, sizeof(struct FindjoB));
   hmboss->subjob= &array_job;
   proxy_job= job;
 } else {
   memcpy(&array_job, job, sizeof(struct FindjoB));
   proxy_job= &array_job;
   hindmost= job;
 }
 array_job.start_path= NULL; /* is owned by the original, not by array_job */

 /* Count matching nodes */
 Xorriso_destroy_node_array(xorriso, 0);
 array_job.action= 30; /* internal: count */
 for(i= 0; i < filec; i++) {
   if(flag & 1) {
     xorriso->node_counter++;
 continue;
   }
   ret= Findjob_set_start_path(proxy_job, filev[i], 0);
   if(ret <= 0)
     goto ex;
   ret= Xorriso_findi(xorriso, proxy_job, NULL, boss_mem, NULL,
                      filev[i],  &dir_stbuf, 0, find_flag);
   if(ret <= 0)
     goto ex;
 }
 if(xorriso->node_counter <= 0)
   {ret= 1; goto ex;}

 mem_needed= boss_mem + xorriso->node_counter * sizeof(IsoNode *);
 if(!(flag &1)) {
   ret= Xorriso_check_temp_mem_limit(xorriso, mem_needed, 0);
   if(ret <= 0) {
     /* Memory curbed : Perform unsorted find jobs */
     if(hmboss != NULL)
       hmboss->subjob= hindmost;
     for(i= 0; i < filec; i++) {
       ret= Findjob_set_start_path(job, filev[i], 0);
       if(ret <= 0)
         goto ex;
       ret= Xorriso_findi(xorriso, job, NULL, boss_mem, NULL,
                          filev[i], &dir_stbuf, 0, find_flag);
       if(ret <= 0)
         if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0)
           goto ex;
     }
     {ret= 1; goto ex;}
   }
 }

 /* Copy matching nodes into allocated array */
 ret= Xorriso_new_node_array(xorriso, xorriso->temp_mem_limit, 0, 0);
 if(ret <= 0)
   goto ex;
 array_job.action= 31; /* internal: register */
 xorriso->node_counter= 0;
 for(i= 0; i < filec; i++) {
   if(flag & 1) {
     ret= Xorriso_get_node_by_path(xorriso, filev[i], NULL, &node, 0);
     if(ret <= 0)
       goto ex;
     if(xorriso->node_counter < xorriso->node_array_size) {
       xorriso->node_array[xorriso->node_counter++]= (void *) node;
       iso_node_ref(node);
     }
 continue;
   }
   ret= Findjob_set_start_path(proxy_job, filev[i], 0);
   if(ret <= 0)
     goto ex;
   ret= Xorriso_findi(xorriso, proxy_job, NULL, mem_needed, NULL,
                      filev[i], &dir_stbuf, 0, find_flag);
   if(ret <= 0)
     goto ex;
 }

 Xorriso_sort_node_array(xorriso, 0);
 if(flag & 2)
   {ret= 1; goto ex;}

 /* Perform job->action on xorriso->node_array */
 for(i= 0; i < xorriso->node_counter; i++) {
   node= xorriso->node_array[i];
   ret= Xorriso_path_from_node(xorriso, node, abs_path, 0);
   if(ret < 0)
     goto ex;
   if(ret == 0)
 continue; /* node is deleted from tree meanwhile */

   ret= Xorriso_findi_action(xorriso, hindmost, NULL, (off_t) 0,
                             abs_path, abs_path, node, 0, 1);
   if(ret <= 0 || xorriso->request_to_abort)
     if(Xorriso_eval_problem_status(xorriso, ret, 1|2)<0)
       goto ex;
 }

 ret= 1;
ex:;
 if(!(flag & (2 | 4)))
   Xorriso_destroy_node_array(xorriso, 0);
 if(hmboss != NULL)
   hmboss->subjob= hindmost;
 if(array_job.start_path != NULL)
   free(array_job.start_path);
 return(ret);
}


/* @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_set_change_pending(xorriso, 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=prepare for a burn run */
int Xorriso_set_abort_severity(struct XorrisO *xorriso, int flag)
{
 int ret, abort_on_number;
 char *sev_text;
 static int note_number= -1, failure_number= -1;

 if(note_number==-1)
   Xorriso__text_to_sev("NOTE", &note_number, 0);
 if(failure_number==-1)
   Xorriso__text_to_sev("FAILURE", &failure_number, 0);
 sev_text= xorriso->abort_on_text;
 ret= Xorriso__text_to_sev(xorriso->abort_on_text, &abort_on_number, 0);
 if(ret<=0)
   return(ret);
 if(abort_on_number<note_number)
   sev_text= "NOTE";
 else if(abort_on_number>failure_number)
   sev_text= "FAILURE";
 ret= iso_set_abort_severity(sev_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);
 strcpy(xorriso->result_line, "libburn OS adapter:  ");
 strncat(xorriso->result_line, burn_scsi_transport_id(0), 1024);
 strcat(xorriso->result_line, "\n");
 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;
 char *manuf= NULL, *media_code1= NULL, *media_code2= NULL;
 char *book_type= NULL, *product_id= NULL;

 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);
 Xorriso_list_profiles(xorriso, 1 | 2);
 if(strstr(profile_name,"BD")==profile_name) {
   printf("Mounted Media: %2.2Xh, %s\n", profile_number, profile_name);
 } else if(strstr(profile_name,"DVD")==profile_name) {
   sprintf(respt, "book type:     %s (emulated booktype)\n", profile_name);
   Xorriso_result(xorriso,1);
   if(profile_number == 0x13) {
     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);
 }

 ret= burn_disc_get_media_id(drive, &product_id, &media_code1, &media_code2,
                                &book_type, 0);
 if(ret > 0 && media_code1 != NULL && media_code2 != NULL)
   manuf= burn_guess_manufacturer(profile_number, media_code1, media_code2, 0);
 if(product_id != NULL) {
   sprintf(respt, "Product Id:    %s\n", product_id);
   Xorriso_result(xorriso,1);
 }
 if(manuf != NULL) {
   sprintf(respt, "Producer:      %s\n", manuf);
   Xorriso_result(xorriso, 1);
 }
 if(profile_number == 0x09 || profile_number == 0x0a) {
   sprintf(respt, "Manufacturer: %s\n", manuf);
   Xorriso_result(xorriso, 1);
 } else if(product_id != NULL && media_code1 != NULL && media_code2 != NULL){
   free(product_id);
   free(media_code1);
   free(media_code2);
   if(book_type != NULL)
     free(book_type);
   product_id= media_code1= media_code2= book_type= NULL;
   ret= burn_disc_get_media_id(drive, &product_id, &media_code1, &media_code2,
                               &book_type, 1);
   if(ret > 0) {
     sprintf(respt, "Manufacturer:  '%s'\n", media_code1);
     Xorriso_result(xorriso, 1);
     if(media_code2[0]) {
       sprintf(respt, "Media type:    '%s'\n", media_code2);
       Xorriso_result(xorriso, 1);
     }
   }
 }
 if(manuf != NULL)
   free(manuf);
 if(media_code1 != NULL)
   free(media_code1);
 if(media_code2 != NULL)
   free(media_code2);
 if(book_type != NULL)
   free(book_type);
 if(product_id != NULL)
   free(product_id);
 return(1);
}


int Xorriso_update_in_sector_map(struct XorrisO *xorriso,
                                 struct SpotlisT *spotlist, int read_chunk,
                                 struct CheckmediajoB *job, int flag)
{
 int sectors, sector_size, sector_blocks, ret;
 struct SectorbitmaP *map;

 Sectorbitmap_destroy(&(xorriso->in_sector_map), 0);
 if(job->use_dev == 1)
   return(1);
 map= job->sector_map;
 sectors= Spotlist_block_count(spotlist, 0);
 if(sectors <= 0)
   return(0);
 sector_size= Spotlist_sector_size(spotlist, read_chunk, 0);
 sector_blocks= sector_size / 2048;
 if(sector_blocks > 1)
   sectors= sectors / sector_blocks + !!(sectors % sector_blocks);
 ret= Sectorbitmap_new(&(xorriso->in_sector_map), sectors, sector_size, 0);
 if(ret <= 0)
   return(ret);
 if(map != NULL)
   Sectorbitmap_copy(map, xorriso->in_sector_map, 0);
 ret= Xorriso_spotlist_to_sectormap(xorriso, spotlist, read_chunk,
                                    &(xorriso->in_sector_map), 1);
 return(ret);
}
 

/*
   @param flag bit0= obtain iso_lba from indev
               bit1= head_buffer already contains a valid head
               bit2= issue message about success
               bit3= check whether source blocks are banned by in_sector_map
*/
int Xorriso_update_iso_lba0(struct XorrisO *xorriso, int iso_lba, int isosize,
                            char *head_buffer, struct CheckmediajoB *job,
                            int flag)
{
 int ret, full_size, i;
 char *headpt;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 off_t seek_ret;

 ret= Xorriso_may_burn(xorriso, 0);
 if(ret <= 0)
   return(0);
 if(flag & 1) {
   ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                 "on attempt to learn current session lba", 1);
   if(ret<=0)
     return(0);
   ret= isoburn_disc_get_msc1(drive, &iso_lba);
   if(ret<=0)
     return(0);
   drive= NULL; /* indev will not be used furtherly */
 }
 if(job == NULL) { 
   ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                  "on attempt to update at lba 0 to 31", 2);
   if(ret<=0)
     return(0);
 }
 if(iso_lba < 32)
   return(2);
 if(!(flag & 2)) {
   /* head_buffer was not filled yet. Read it from output media. */
   if(job != NULL && job->data_to_fd >= 0) {
     if((flag & 8) && job->sector_map != NULL) {
       ret= Sectorbitmap_bytes_are_set(job->sector_map,
                     ((off_t) iso_lba) * (off_t) 2048,
                     ((off_t) (iso_lba + 32)) * ((off_t) 2048) - (off_t) 1, 0);
       if(ret <= 0) {
         sprintf(xorriso->info_text,
           "ISO image head at lba %d is marked as invalid blocks in file copy",
           iso_lba); 
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",
                             0);
          return(0);
       }
     }
     seek_ret= lseek(job->data_to_fd, ((off_t) 2048) * (off_t) iso_lba,
                     SEEK_SET);
     if(seek_ret == -1)
       ret= 0;
     else
       ret= read(job->data_to_fd, head_buffer, 64 * 1024); 
     if(ret < 64 * 1024) {
       Xorriso_process_msg_queues(xorriso,0);
       sprintf(xorriso->info_text,
               "Cannot read ISO image head from file copy"); 
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0);
       return(0);
     }
   } else {
     ret= isoburn_read_iso_head(drive, iso_lba, &isosize, head_buffer, 2);
     if(ret<=0) {
       Xorriso_process_msg_queues(xorriso,0);
       sprintf(xorriso->info_text,
               "Cannot read freshly written ISO image head"); 
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       return(0);
     }
   }
 }
 /* patch ISO header */
 full_size= iso_lba + isosize;
 headpt= head_buffer + 32*1024;
 for(i=0;i<4;i++)
   headpt[87-i]= headpt[80+i]= (full_size >> (8*i)) & 0xff;

 if(job != NULL) {
   seek_ret= lseek(job->data_to_fd, (off_t) 0, SEEK_SET);
   if(seek_ret == -1)
     ret= 0;
   else
     ret= write(job->data_to_fd, head_buffer, 64 * 1024); 
   if(ret < 64 * 1024) {
     Xorriso_process_msg_queues(xorriso,0);
     sprintf(xorriso->info_text,
             "Cannot write ISO image head to file copy"); 
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",0);
     return(0);
   }
 } else {
   ret= burn_random_access_write(drive, (off_t) 0, head_buffer,
                                 (off_t) (64*1024), 1);
   if(ret<=0) {
     Xorriso_process_msg_queues(xorriso,0);
     sprintf(xorriso->info_text,
             "Cannot write new ISO image head to LBA 0"); 
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     return(0);
   }
 }
 if(flag & 4) {
   sprintf(xorriso->info_text,
           "Overwrote LBA 0 to 31 by 64 KiB from LBA %d", iso_lba); 
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
 }
 return(1);
}


/* @param write_start_address  is valid if >=0    
   @param tsize                is valid if >0
   @param flag bit0= grow_overwriteable_iso
               bit1= do_isosize
               bit2= do_xa1 conversion
*/
int Xorriso_burn_track(struct XorrisO *xorriso, off_t write_start_address,
                       char *track_source, off_t tsize, int flag)
{
 int ret, fd, unpredicted_size, profile_number, is_cd= 0, dummy, nwa= -1;
 int isosize= -1, do_isosize, is_bd= 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= NULL, *fifo_src= NULL;
 enum burn_disc_status disc_state;
 char reasons[BURN_REASONS_LEN], sfe[5*SfileadrL], profile_name[80];
 char head_buffer[64*1024];

 ret= Xorriso_may_burn(xorriso, 0);
 if(ret <= 0)
   return(0);
 ret= Xorriso_auto_format(xorriso, 0);
 if(ret <=0 )
   return(0);

 do_isosize= !!(flag&2);
 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) {
   sprintf(xorriso->info_text, "Cannot add session object to disc object.");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   goto ex;
 }
 track= burn_track_create();
 if(track_source[0] == '-' && track_source[1] == 0) {
   fd= 0;
 } else {
   if(xorriso->fs >= 64)
     fd= burn_os_open_track_src(track_source, O_RDONLY, 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;

 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;
 }
 if((do_isosize || xorriso->fs != 0) && xorriso->fs < 64)
   xorriso->fs= 64;
 if(xorriso->fs > 0) {
   fifo_src= burn_fifo_source_new(data_src, 2048 + 8 * !!(flag & 4),
                                  xorriso->fs, 1);
   if(fifo_src == NULL) {
     sprintf(xorriso->info_text, "Could not create fifo object of %.f MB",
             ((double) xorriso->fs) / 1024.0 / 1024.0);
     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 == NULL ? data_src : 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_track_set_cdxa_conv(track, !!(flag & 4));
 burn_session_add_track(session, track, BURN_POS_END);
 burn_source_free(data_src);

 if(flag&1)
   /* consider overwriteables with ISO as appendable */
   disc_state= isoburn_disc_get_status(drive);
 else
   /* handle overwriteables as always blank */
   disc_state= burn_disc_get_status(drive);

 if(disc_state == BURN_DISC_BLANK || disc_state == BURN_DISC_APPENDABLE) {
   /* ok */;
 } else {
   if(disc_state == BURN_DISC_FULL) {
     sprintf(xorriso->info_text,
          "Closed media with data detected. Need blank or appendable media.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     if(burn_disc_erasable(drive)) {
       sprintf(xorriso->info_text, "Try -blank as_needed\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(isoburn_needs_emulation(drive))
   burn_write_opts_set_multi(burn_options, 0);

 if(tsize > 0) {
   fixed_size= tsize;
   burn_track_set_size(track, fixed_size);
 }
 if(do_isosize) {
   ret= burn_fifo_peek_data(xorriso->pacifier_fifo, head_buffer, 64*1024, 0);
   if(ret<=0) {
     Xorriso_process_msg_queues(xorriso,0);
     sprintf(xorriso->info_text,
             "Cannot obtain first 64 kB from input stream."); 
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }
   /* read isosize from head_buffer, not from media*/
   ret= isoburn_read_iso_head(drive, 0, &isosize, head_buffer, (1<<13));
   if(ret<=0) {
     Xorriso_process_msg_queues(xorriso,0);
     sprintf(xorriso->info_text,
             "Option -isosize given but data stream seems not to be ISO 9660"); 
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }
   sprintf(xorriso->info_text, "Size of ISO 9660 image: %ds", isosize);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   fixed_size= ((off_t) (isosize)) * (off_t) 2048;
   burn_track_set_size(track, fixed_size);
 }

 ret= Xorriso_get_profile(xorriso, &profile_number, profile_name, 2);
 is_cd= (ret==2);
 is_bd= (ret == 3);

 if(isoburn_needs_emulation(drive)) {
   if(flag&1) {
     ret= isoburn_disc_track_lba_nwa(drive, burn_options, 0, &dummy, &nwa);
     if(ret<=0) {
       Xorriso_process_msg_queues(xorriso,0);
       sprintf(xorriso->info_text,
     "Cannot obtain next writeable address of emulated multi-session media\n");
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       ret= 0; goto ex;
     }
     if(nwa==32)
       nwa= 0; /* No automatic toc emulation. Formatter might not be aware. */
     burn_write_opts_set_start_byte(burn_options,((off_t) nwa) * (off_t) 2048);
   } else {
     nwa= 0;
     burn_write_opts_set_start_byte(burn_options, (off_t) 0);
   }
 }

 if(write_start_address>=0) {
   nwa= write_start_address / (off_t) 2048;
   if(((off_t) nwa) * (off_t) 2048 < write_start_address )
     nwa++;
   burn_write_opts_set_start_byte(burn_options, ((off_t) nwa) * (off_t) 2048);
 }

 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_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, 2 | (is_cd << 4) | (is_bd << 5));
 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;
 }

 if(flag & 1) {
   ret= Xorriso_update_iso_lba0(xorriso, nwa, isosize, head_buffer, NULL,
                                flag & 2);
   if(ret <= 0)
     goto ex;
 }
 sprintf(xorriso->info_text, "Writing to %s completed sucessfully.\n\n",
         Text_shellsafe(xorriso->outdev,sfe,0));
 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
                                                     3= ok, is BD 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(((flag&2) && xorriso->out_drive_handle==NULL) ||
    ((!(flag&2)) && xorriso->in_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);
 if(*profile_number == 0x40 || *profile_number == 0x41 ||
    *profile_number == 0x42 || *profile_number == 0x43)
   return(3);
 return(0);
}



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

 *stream= NULL;
 if(flag&1) {
   node= (IsoNode *) node_pt;
 } else {
   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);
 }

 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);
 }
 if(flag & 2) {
   /* Dig out the most original stream */
   while(1) {
     input_stream= iso_stream_get_input_stream(iso_stream, 0);
     if(input_stream == NULL)
   break;
     iso_stream= input_stream;
   }
 }
 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);
   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);
 }
 Xorriso_process_msg_queues(xorriso,0);
 *stream= iso_stream;

#ifdef NIX
 /* <<< */
 {
   unsigned int fs_id;
   dev_t dev_id;
   ino_t ino;

   iso_stream_get_id(iso_stream, &fs_id, &dev_id, &ino);
   fprintf(stderr, "xorriso_debug: iso_ino= %ld\n", (long int) ino);
 }
#endif

 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
               bit2= recognize and parse split parts despite
                     xorrio->split_size <= 0
*/
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, incomplete= 0, overlapping= 0;
 int partno, total_parts, first_total_parts= -1;
 off_t offset, bytes, total_bytes, first_total_bytes= -1, first_bytes= -1;
 off_t size, covered;

 IsoImage *volume;
 IsoDir *dir_node;
 IsoDirIter *iter= NULL;
 IsoNode *node;
 char *name;
 struct stat stbuf, first_stbuf;

 *count= 0;
 *parts= NULL;

 if(xorriso->split_size <= 0 && !(flag & 4))
   return(0);

 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;} 
   if(offset != first_bytes * (off_t) (partno - 1))
     {ret= 0; goto ex;}
   (*count)++;
 }
 if(*count <= 0 || *count != first_total_parts)
   {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;
 }

 Splitparts_sort(*parts, *count, 0);

 covered= 0;
 for(i= 0; i<*count; i++) {
   Splitparts_get(*parts, i, &name, &partno, &total_parts, &offset, &bytes,
                  &total_bytes, 0);
   if(offset>covered)
     incomplete= 1;
   else if(offset<covered)
     overlapping= 1;
   if(offset+bytes > covered)
     covered= offset+bytes;
 }
 if(total_bytes>covered)
   incomplete= 1;
 memcpy(total_stbuf, &first_stbuf, sizeof(struct stat));
 total_stbuf->st_size= total_bytes;
 ret= !(overlapping || incomplete);
ex:;
 if(iter!=NULL)
   iso_dir_iter_free(iter);
 return(ret);
}


/* @param flag bit0= node is valid, do not resolve path
               bit1= insist in complete collection of part files
*/
int Xorriso_is_split(struct XorrisO *xorriso, char *path, void *node,
                     int flag)
{
 struct SplitparT *split_parts= NULL;
 int split_count= 0, ret;
 struct stat stbuf;

 ret= Xorriso_identify_split(xorriso, path, node, &split_parts,
                             &split_count, &stbuf, flag & 3);
 if(split_parts!=NULL)
   Splitparts_destroy(&split_parts, split_count, 0);
 return(ret>0);
}


/* @param flag bit0= grow_overwriteable_iso
               bit1= obtain info from outdev
               bit2= no need to obtain msc2 (NWA)
*/
int Xorriso_msinfo(struct XorrisO *xorriso, int *msc1, int *msc2, int flag)
{
 int ret, dummy;
 struct burn_drive *drive;
 struct burn_drive_info *dinfo;
 enum burn_disc_status disc_state;

 *msc1= *msc2= -1;
 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to obtain msinfo", flag&2);
 if(ret<=0)
   return(ret);

 if(flag&1)
   disc_state= isoburn_disc_get_status(drive);
 else
   disc_state= burn_disc_get_status(drive);
 if(disc_state != BURN_DISC_APPENDABLE &&
    !(disc_state == BURN_DISC_FULL && (flag & 4))) {
   Xorriso_process_msg_queues(xorriso,0);
   if(!(flag & 4)) {
     sprintf(xorriso->info_text,
             "%s media is not appendable. Cannot obtain -msinfo.",
             (flag&2) ? "Output" : "Input");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   }
   return(0);
 }
 ret= isoburn_disc_get_msc1(drive, msc1);
 if(ret<=0) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text, "Cannot obtain address of most recent session");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }
 if(flag & 4)
   return(1);
 ret= isoburn_disc_track_lba_nwa(drive, NULL, 0, &dummy, msc2);
 if(ret<0) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text, "Cannot obtain next writeable address on media");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }
 return(1);
}

/* @param flag bit0= this is a follow-up session (i.e. on CD: TAO)
               bit1= no pacifier messages
               bit2= compute stream MD5 and look out for checksum tag
   @return <=0 error, 1= done, 2= aborted due to limit
*/
int Xorriso_check_interval(struct XorrisO *xorriso, struct SpotlisT *spotlist,
                           struct CheckmediajoB *job,
                           int from_lba, int block_count, int read_chunk,
                           int md5_start, int flag)
{
 int i, j, ret, total_count= 0, sectors= -1, sector_size= -1, skip_reading;
 int prev_quality= -1, quality= -1, retry= 0, profile_no, is_cd= 0;
 int start_sec, end_sec, first_value, fret;
 char profile_name[80];
 int start_lba= 0;
 struct burn_drive *drive;
 struct burn_drive_info *dinfo;
 char data[64*1024], sfe[5*SfileadrL];
 off_t data_count, to_read, read_count= 0, write_amount;
 double pre_read_time, post_read_time, time_diff, total_time_diff= 0;
 double last_abort_file_time= 0;
 struct stat stbuf;
 void *ctx= NULL, *cloned_ctx= NULL;
 char md5[16], tag_md5[16];
 uint32_t pos, range_start, range_size, next_tag= 0, lba, md5_spot_lba= 0;
 int md5_spot_value= Xorriso_read_quality_untesteD, chain_broken= 0;
 int tag_type= 0, valid, was_sb_tag= 0, in_track_gap= 0;
 char *comparison= "", *sev_text= "DEBUG", *tag_type_name= "";

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to check media readability",
                                2 * !!job->use_dev);
 if(ret<=0)
   goto ex;
 ret= burn_disc_get_profile(drive, &profile_no, profile_name);
 if(ret > 0)
   if(profile_no >= 0x08 && profile_no <= 0x0a)
     is_cd= 1;

 if(job->sector_map != NULL) {
   Sectorbitmap_get_layout(job->sector_map, &sectors, &sector_size, 0);
   sector_size/= 2048;
 }

 if(job->retry > 0)
   retry= 1;
 else if(job->retry == 0 && is_cd)
   retry= 1;

 if(flag & 4) {
   ret= iso_md5_start(&ctx);
   if(ret < 0) {
     Xorriso_no_malloc_memory(xorriso, NULL, 0);
     ret= -1; goto ex;
   }
 }

 start_lba= from_lba;
 to_read= read_chunk;
 post_read_time= Sfile_microtime(0);
 for(i= 0; i < block_count; i+= to_read) {
   skip_reading= 0;

   if(job->abort_file_path[0]) {
     if(post_read_time - last_abort_file_time >= 0.1) {
       if(stat(job->abort_file_path, &stbuf) != -1) {
         if(stbuf.st_mtime >= xorriso->start_time) {
           sprintf(xorriso->info_text,
                   "-check_media: Found fresh abort_file=%s",
                   job->abort_file_path);
           Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
           goto abort_check;
         }
       }
       last_abort_file_time= post_read_time;
     }
   }
   if(job->item_limit > 0 &&
      Spotlist_count(spotlist, 0) + 2 >= job->item_limit) {
     sprintf(xorriso->info_text, "-check_media: Reached item_limit=%d",
             job->item_limit);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
     goto abort_check;
   }
   pre_read_time= Sfile_microtime(0);
   if(job->time_limit > 0
	 && job->start_time + job->time_limit < pre_read_time) {
     sprintf(xorriso->info_text, "-check_media: Reached time_limit=%d",
             job->time_limit);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
abort_check:;
     if(prev_quality >= 0) {
       ret= Spotlist_add_item(spotlist, start_lba, i + from_lba - start_lba,
                              prev_quality, 0);
       if(ret <= 0)
         goto ex;
     }
     ret= Spotlist_add_item(spotlist, i + from_lba, block_count - i,
                            Xorriso_read_quality_untesteD, 0);
     if(ret > 0)
       ret= 2;
     goto ex;
   }

   to_read= read_chunk;
   if(i + to_read > block_count)
     to_read= block_count - i;
   if(is_cd && i + to_read + 2 >= block_count) {
     /* Read last 2 blocks of CD track separately, because with TAO tracks
        they are always unreadable but with SAO tracks they contain data.
     */
     if(to_read > 2)
       to_read-= 2;
     else if(to_read > 1) {
       if(flag & 1) {
         quality= Xorriso_read_quality_tao_enD;
         skip_reading= 1;
       } else
         to_read--;
     }
   }

   if(sector_size == read_chunk && from_lba % read_chunk == 0 
      && !skip_reading) {
     if(Sectorbitmap_is_set(job->sector_map, (i + from_lba) / sector_size, 0)){
       quality= Xorriso_read_quality_valiD;
       skip_reading= 1;
     }
   } else if(sector_size > 0 && !skip_reading) {
     start_sec= (i + from_lba) / sector_size;
     end_sec= (i + to_read + from_lba) / sector_size;
     first_value= Sectorbitmap_is_set(job->sector_map, start_sec, 0);
     for(j= start_sec; j < end_sec; j++)
       if(Sectorbitmap_is_set(job->sector_map, j, 0) != first_value)
     break;
     to_read= j * sector_size - i - from_lba;
     skip_reading= !!first_value;
     if(skip_reading)
       quality= Xorriso_read_quality_valiD;
   }

   if(skip_reading) {
     pre_read_time= post_read_time= Sfile_microtime(0);
   } else {
     data_count= 0;
     pre_read_time= Sfile_microtime(0);
     ret= burn_read_data(drive, ((off_t) (i + from_lba)) * (off_t) 2048, data,
                         to_read * (off_t) 2048, &data_count, 4 * !retry);
     post_read_time= Sfile_microtime(0);
     time_diff= post_read_time - pre_read_time;
     total_time_diff+= time_diff;
     total_count++;
     if(ret <= 0) {
       Xorriso_process_msg_queues(xorriso,0);
       if(data_count / 2048 < to_read) {
         if(data_count > 0 && retry) {
           if(prev_quality >= 0) {
             ret= Spotlist_add_item(spotlist, start_lba,
                                    i + from_lba - start_lba, prev_quality, 0);
              if(ret <= 0)
                goto ex;
           }
           ret= Spotlist_add_item(spotlist, i + from_lba, data_count / 2048,
                                  Xorriso_read_quality_partiaL, 0);
           if(ret <= 0)
             goto ex;
           start_lba= i + from_lba + data_count / 2048;
           prev_quality= Xorriso_read_quality_unreadablE;
         }
         quality= Xorriso_read_quality_unreadablE;
         if(retry)
           to_read= data_count / 2048 + 1;
       } else
         quality= Xorriso_read_quality_partiaL;
       fret= Xorriso_eval_problem_status(xorriso, ret, 1|2);
       if(fret<0)
         goto ex;
     } else {
       quality= Xorriso_read_quality_gooD;
       if(time_diff > job->slow_threshold_seq && job->slow_threshold_seq > 0 &&
          i > 0)
         quality= Xorriso_read_quality_sloW;
     }

     /* MD5 checksumming */
     if(ctx != NULL) {
       for(j= 0; j < to_read; j++) {
         lba=  i + j + from_lba;
         if(lba < md5_start)
       continue;
         ret= 0;
         if(lba > md5_start + 16 &&
            (next_tag == 0 || chain_broken || lba == next_tag)) {
           ret= iso_util_decode_md5_tag(data + j * 2048, &tag_type,
                                        &pos, &range_start, &range_size,
                                        &next_tag, tag_md5, !!chain_broken);
         }
         valid= (ret == 1 || ret == ISO_MD5_AREA_CORRUPTED) && pos == lba;
         if(valid && tag_type == 2 && (lba < md5_start + 32 || in_track_gap)) {
           tag_type_name= "superblock";
           was_sb_tag= 1;
           if(in_track_gap && range_start != md5_start && range_start < lba &&
              lba - range_start <= j) {
             /* Looking for next session : start computing in hindsight.
                Session start and superblock tag are supposed to be in the
                same 64 kB chunk.
             */
             iso_md5_end(&ctx, md5);
             ret= iso_md5_start(&ctx);
             if(ret < 0) {
               Xorriso_no_malloc_memory(xorriso, NULL, 0);
               ret= -1; goto ex;
             }
             iso_md5_compute(&ctx, data + (j - (lba - range_start)) * 2048,
                             (lba - range_start) * 2048);
             md5_start= range_start;
             in_track_gap= 0;
           }
         } else if(valid && tag_type == 4 && lba < 32) {
           tag_type_name= "relocated 64kB superblock";
         }else if(valid && tag_type == 3 && was_sb_tag) {
           tag_type_name= "tree";
         }else if(valid && tag_type == 1) {

           /* >>> ??? allow this without superblock and tree tag ? */

           tag_type_name= "session";
         } else {
           tag_type_name= "";
         }
         if (tag_type_name[0]) {
           if(range_start != md5_start) {
             sprintf(xorriso->info_text,
                 "Found MD5 %s tag which covers different data range",
                 tag_type_name);
             Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE",0);
               sprintf(xorriso->info_text,
                       "              Expected: %u  Found: %u",
                       (unsigned int) md5_start, range_start);
             Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE",0);
             chain_broken= 1;
             valid= 0;
           } else {
             ret= iso_md5_clone(ctx, &cloned_ctx);
             if(ret <= 0) {
               Xorriso_no_malloc_memory(xorriso, NULL, 0);
               ret= -1; goto ex;
             }
             iso_md5_end(&cloned_ctx, md5);

             if(ret == ISO_MD5_AREA_CORRUPTED) {
               comparison= "CORRUPTED";
               sev_text= "WARNING";
               md5_spot_value= Xorriso_read_quality_md5_mismatcH;
               chain_broken= 1;
             } else if(! iso_md5_match(tag_md5, md5)) {
               comparison= "NON-MATCHING";
               sev_text= "WARNING";
               md5_spot_value= Xorriso_read_quality_md5_mismatcH;
               chain_broken= 1;
             } else {
               comparison= "matching";
               sev_text= "UPDATE";
               md5_spot_value= Xorriso_read_quality_md5_matcH;
             }
             md5_spot_lba= lba;
             sprintf(xorriso->info_text,
                     "Found %s MD5 %s tag: start=%d size=%d",
                     comparison, tag_type_name, md5_start, lba - md5_start);
             Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0,
                                 sev_text, 0);
           }
           if(valid && (tag_type == 1 || 
                        (tag_type == 4 && pos == lba && lba < 32))) {
             if(md5_spot_value != Xorriso_read_quality_untesteD) {
               ret= Spotlist_add_item(spotlist, md5_start,
                                  md5_spot_lba - md5_start, md5_spot_value, 0);
               if(ret <= 0)
               goto ex;
             }
             md5_spot_value= Xorriso_read_quality_untesteD;
             md5_start = lba + 1;
             if (md5_start % 32)
                 md5_start= md5_start + (32 - (md5_start % 32));
             next_tag= 0;

             iso_md5_end(&ctx, md5);
             ret= iso_md5_start(&ctx);
             if(ret < 0) {
               Xorriso_no_malloc_memory(xorriso, NULL, 0);
               ret= -1; goto ex;
             }
             if(tag_type == 1)
               in_track_gap= 1;
       continue;
           }
         }
         iso_md5_compute(ctx, data + j * 2048, 2048);
       }
     }

     write_amount= data_count;
     if(data_count > 0) {
       read_count+= data_count;
       if(job->data_to_limit >= 0 && read_count > job->data_to_limit)
         write_amount-= (read_count - job->data_to_limit);
     }
     if(write_amount > 0) {
       if(job->data_to_fd >= 0) {
         ret= lseek(job->data_to_fd,
                 ((off_t) (i + from_lba)) * (off_t) 2048 + job->data_to_offset,
                 SEEK_SET);
         if(ret == -1) {
failed_to_write:;
           sprintf(xorriso->info_text, "Cannot write %d bytes to lba %d of %s",
                   (int) data_count, i + from_lba,
                   Text_shellsafe(job->data_to_path, sfe, 0));
           Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno,
                               "FAILURE", 0);
           {ret= 0; goto ex;}
         }
         ret= write(job->data_to_fd, data, write_amount);
         if(ret == -1)
           goto failed_to_write;
       }
     }
   }
   if(quality != prev_quality) {
     if(prev_quality >= 0) {
       ret= Spotlist_add_item(spotlist, start_lba, i + from_lba - start_lba,
                              prev_quality, 0);
       if(ret <= 0)
         goto ex;
     }
     start_lba= i + from_lba;
     prev_quality= quality;
   }
   if(!(flag & 2)) {
     xorriso->pacifier_count+= to_read;
     if(post_read_time - xorriso->last_update_time >=
        xorriso->pacifier_interval)
       Xorriso_pacifier_callback(xorriso, "sectors examined",
                      xorriso->pacifier_count, xorriso->pacifier_total, "", 0);
   }
 }
 if(prev_quality >= 0) {
   ret= Spotlist_add_item(spotlist, start_lba,
                          block_count + from_lba - start_lba, prev_quality, 0);
   if(ret <= 0)
     goto ex;
 }

 /* <<< for calibration of quality */
 if(total_count > 0) {
   sprintf(xorriso->info_text, "Xorriso_check_interval: %.1f s / %d = %f",
           total_time_diff, total_count, total_time_diff / total_count);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
 }


 /* MD5 checksumming : register result */

 /* >>> ??? allow chain_broken to be a match ? */

 if(next_tag > 0) {
   sprintf(xorriso->info_text, "Missing announced MD5 tag: start=%d pos=%d",
           md5_start, next_tag);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0);
   md5_spot_value= Xorriso_read_quality_md5_mismatcH;
   md5_spot_lba= next_tag;
 }
 if(md5_spot_value != Xorriso_read_quality_untesteD) {
   ret= Spotlist_add_item(spotlist, md5_start, md5_spot_lba - md5_start,
                          md5_spot_value, 0);
   if(ret <= 0)
     goto ex;
 }

 ret= 1;
ex:
 if(ctx != NULL)
   iso_md5_end(&ctx, md5);
 return(ret);
}


int Xorriso_check_media(struct XorrisO *xorriso, struct SpotlisT **spotlist,
                        struct CheckmediajoB *job, int flag)
{
 int media_blocks= 0, read_chunk= 16, ret, mode, start_lba= 0;
 int blocks, os_errno, i, j, last_track_end= -1, track_blocks, track_lba;
 int num_sessions, num_tracks, declare_untested= 0, md5_start;
 int read_capacity= -1, end_lba, hret, count, quality;
 char *toc_info= NULL;
 struct burn_drive *drive;
 struct burn_drive_info *dinfo;
 struct isoburn_toc_disc *isoburn_disc= NULL;
 struct isoburn_toc_session **isoburn_sessions;
 struct isoburn_toc_track **iso_burn_tracks;
 struct burn_toc_entry isoburn_entry;
 struct stat stbuf;
 struct burn_multi_caps *caps= NULL;

 *spotlist= NULL;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to check media readability",
                                2 * !!job->use_dev);
 if(ret<=0)
   goto ex;
 
 /* >>> determine media type dependent blocking factor:
        32 kB for CD (with 2kB retry) and DVD, 64 kB for BD
        eventually adjust read_chunk
 */;
 if(job->min_block_size != 0)
   read_chunk= job->min_block_size;

 ret= Spotlist_new(spotlist, 0);
 if(ret <= 0)
   {ret= -1; goto ex;}

 if(job->sector_map_path[0]) {
   Sectorbitmap_destroy(&(job->sector_map), 0);
   if(stat(job->sector_map_path, &stbuf) != -1) {
     ret= Sectorbitmap_from_file(&(job->sector_map), job->sector_map_path,
                                 xorriso->info_text, &os_errno, 0);
     if(ret <= 0) {
       if(xorriso->info_text[0])
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, os_errno,
                             "FAILURE", 0);
       goto ex;
     }
   }
   Xorriso_toc_to_string(xorriso, &toc_info, 4 * !job->map_with_volid);
 }
 ret= Xorriso_open_job_data_to(xorriso, job, 0);
 if(ret <= 0)
   goto ex;
 Xorriso_pacifier_reset(xorriso, 0);
 job->start_time= time(NULL);
 mode= job->mode;
 if(job->min_lba > 0) {
   start_lba= job->min_lba;
   ret= Spotlist_add_item(*spotlist, 0, job->min_lba, 
                          Xorriso_read_quality_untesteD, 0);
   if(ret <= 0)
     goto ex;
 }
 ret= burn_get_read_capacity(drive, &read_capacity, 0);
 if(ret <= 0)
   read_capacity= -1;

 if(job->max_lba >= 0) {
   blocks= job->max_lba + 1 - start_lba;
   xorriso->pacifier_total= blocks;
   ret= Xorriso_check_interval(xorriso, *spotlist, job, start_lba, blocks,
                               read_chunk, 0, 0);
   if(ret <= 0)
     goto ex;
   
 } else if(mode == 0) { /* track by track */
   isoburn_disc= isoburn_toc_drive_get_disc(drive);
   if(isoburn_disc == NULL)
     goto no_content_visible;
   isoburn_sessions=
                    isoburn_toc_disc_get_sessions(isoburn_disc, &num_sessions);
   for(i= 0; i < num_sessions; i++) {
     iso_burn_tracks= isoburn_toc_session_get_tracks(isoburn_sessions[i],
                                                     &num_tracks);
     for(j= 0; j < num_tracks; j++) {
       isoburn_toc_track_get_entry(iso_burn_tracks[j], &isoburn_entry);
       if(!(isoburn_entry.extensions_valid & 1)) /* should not happen */
     continue;
       track_lba= isoburn_entry.start_lba;
       track_blocks= isoburn_entry.track_blocks;
       md5_start= track_lba;
       if(i == 0 && j == 0) {
         if(track_lba == 32) {
           ret= burn_disc_get_multi_caps(drive, BURN_WRITE_NONE, &caps, 0);
           if(ret > 0) {
             if(caps->start_adr) {
               /* block 0 to 31 are the overall mount entry of overwriteable */
               track_lba= 0;
               track_blocks+= 32;
             }
           }
         }
       }
       if(last_track_end >= 0 && last_track_end < track_lba &&
          last_track_end >= start_lba) {
         ret= Spotlist_add_item(*spotlist, last_track_end,
                                track_lba - last_track_end,
                                Xorriso_read_quality_off_tracK, 0);
         if(ret <= 0)
           goto ex;
         xorriso->pacifier_count+= track_lba - last_track_end;
       }
       last_track_end= track_lba + track_blocks;

       if(track_lba < start_lba) {
         track_blocks-= start_lba - track_lba;
         track_lba= start_lba;
       }
       if(track_blocks <= 0)
     continue;
       if(declare_untested) {
         ret= Spotlist_add_item(*spotlist, track_lba, track_blocks,
                                Xorriso_read_quality_untesteD, 0);
         if(ret <= 0)
           goto ex;
       } else {
         ret= Xorriso_check_interval(xorriso, *spotlist, job, track_lba,
                                     track_blocks, read_chunk, md5_start,
                                     (i > 0) | (4 * (xorriso->do_md5 & 1)));
         if(ret <= 0)
           goto ex;
         if(ret == 2)
           declare_untested= 1;
       }
     }
   }

 } else if(mode == 1) { /* isoburn disc capacity */
   isoburn_disc= isoburn_toc_drive_get_disc(drive);
   if(isoburn_disc == NULL) {
no_content_visible:;
     Xorriso_process_msg_queues(xorriso,0);
     sprintf(xorriso->info_text, "No content detected on media");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     {ret= 0; goto ex;}
   }
   blocks= media_blocks= isoburn_toc_disc_get_sectors(isoburn_disc);
   if(start_lba >= 0)
     blocks-= start_lba;
   if(media_blocks <= 0)
     goto no_content_visible;
   xorriso->pacifier_total= blocks;
   ret= Xorriso_check_interval(xorriso, *spotlist, job, start_lba, blocks,
                               read_chunk, 0, 0);
   if(ret <= 0)
     goto ex;
 } else if(mode == 2) {

   /* >>> single sweep over libburn media capacity */;

 }

 Xorriso_pacifier_callback(xorriso, "sectors examined",
                      xorriso->pacifier_count, xorriso->pacifier_total, "", 1);
 ret= 1;
ex:;

 if(job->data_to_fd != -1)
   close(job->data_to_fd);
 job->data_to_fd= -1;
 
 if(read_capacity >= 0) {
   count= Spotlist_count(*spotlist, 0);
   end_lba= 0;
   for(i= 0; i < count; i++) {
     Spotlist_get_item(*spotlist, i, &start_lba, &blocks, &quality, 0);
     if(start_lba + blocks > end_lba)
       end_lba= start_lba + blocks;
   }
   if(read_capacity > end_lba) {
     hret= Spotlist_add_item(*spotlist, end_lba, read_capacity - end_lba, 
                             Xorriso_read_quality_untesteD, 0);
     if(hret < ret)
       ret= hret;
   }
 }

 if(ret > 0)
   ret= Xorriso_update_in_sector_map(xorriso, *spotlist, read_chunk, job, 0);
 
 if(ret > 0) {
   ret= Xorriso_spotlist_to_sectormap(xorriso, *spotlist, read_chunk,
                                    &(job->sector_map), !!job->untested_valid);
   if(ret > 0 && job->sector_map_path[0]) {
     ret= Sectorbitmap_to_file(job->sector_map, job->sector_map_path, toc_info,
                               xorriso->info_text, &os_errno, 0);
     if(ret <= 0) {
       if(xorriso->info_text[0])
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, os_errno,
                             "FAILURE", 0);
     }
   }
 }
 if(toc_info != NULL)
    free(toc_info);
 if(ret <= 0)
   Spotlist_destroy(spotlist, 0);
 if(caps!=NULL)
   burn_disc_free_multi_caps(&caps);
 return(ret);
}


/* @param flag bit1= for Xorriso_check_interval(): no pacifier messages
*/
int Xorriso_read_file_data(struct XorrisO *xorriso, IsoNode *node,
                           char *img_path, char *disk_path,
                           off_t img_offset, off_t disk_offset,
                           off_t bytes, int flag)
{
 int ret, i, lba_count= 0, *start_lbas= NULL, *end_lbas= NULL, read_chunk= 16;
 int lba, count, blocks, quality, spot, bad_extract= 0;
 off_t size= 0, file_base_bytes= 0, file_processed_bytes= 0, img_adr;
 off_t new_file_base_bytes, upto_file_bytes, start_byte= 0;
 char sfe[5*SfileadrL];
 struct SpotlisT *spotlist= NULL;
 struct CheckmediajoB *job= NULL;

 upto_file_bytes= img_offset + bytes;

 /* >>> make Xorriso_check_interval() ready for copying in byte granularity */
 if(img_offset % (off_t) 2048) {
   sprintf(xorriso->info_text,
           "Image address offset is not a multiple of 2048");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }

 ret= Xorriso__start_end_lbas(node, &lba_count, &start_lbas, &end_lbas, &size,
                              0);
 if(ret <= 0) {
   Xorriso_process_msg_queues(xorriso,0);
   sprintf(xorriso->info_text,
           "File object %s is currently not a data file from the loaded image",
           Text_shellsafe(img_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   goto ex;
 }
 if(img_offset + bytes < size && bytes > 0)
   size= img_offset + bytes;

 ret= Checkmediajob_new(&job, 0);
 if(ret <= 0)
   goto ex;
 if(xorriso->check_media_default != NULL)
   Checkmediajob_copy(xorriso->check_media_default, job, 0);
 job->min_lba= -1;
 job->max_lba= -1;
 job->sector_map_path[0]= 0;

 ret= Spotlist_new(&spotlist, 0);
 if(ret <= 0)
   {ret= -1; goto ex;}

 if(Sfile_str(job->data_to_path, disk_path, 0) <= 0)
   {ret= -1; goto ex;}
 Xorriso_open_job_data_to(xorriso, job, 0);
 if(ret <= 0)
   goto ex;

 for(i= 0; i < lba_count && file_base_bytes < upto_file_bytes; i++) {
   lba= start_lbas[i];
   count= end_lbas[i] + 1 - start_lbas[i];
   new_file_base_bytes= file_base_bytes + ((off_t) count) * (off_t) 2048;

   /* skip intervals before img_offset */
   if(new_file_base_bytes <= img_offset) {
     file_base_bytes= new_file_base_bytes;
 continue;
   }
   /* Eventually adjust first interval start */
   img_adr= ((off_t) lba) * (off_t) 2048;
   if(file_base_bytes < img_offset) {
     img_adr+= img_offset - file_base_bytes;
     lba= img_adr / (off_t) 2048;
     count= end_lbas[i] + 1 - lba;
     file_base_bytes= img_offset;
   }

   /* Eventually omit surplus blocks */
   if(new_file_base_bytes > upto_file_bytes)
     count-= (new_file_base_bytes - upto_file_bytes) / (off_t) 2048;
   /* Adjust job */
   job->data_to_offset= file_processed_bytes - img_adr + disk_offset;
   job->data_to_limit= size - file_base_bytes;

   file_processed_bytes+= ((off_t) count) * (off_t) 2048;
   ret= Xorriso_check_interval(xorriso, spotlist, job, lba, count, read_chunk,
                               0, (flag & 2));
   if(ret <= 0)
     goto ex;
   if (ret == 2) {
     sprintf(xorriso->info_text, "Attempt aborted to extract data from %s",
             Text_shellsafe(img_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }
   file_base_bytes= new_file_base_bytes;
 } 

 /* Use spotlist to evaluate damage */
 file_base_bytes= 0;
 count= Spotlist_count(spotlist, 0);
 for(spot= 0; spot < count; spot++) {
   ret= Spotlist_get_item(spotlist, spot, &lba, &blocks, &quality, 0);
   if(ret <= 0)
 continue;
   if(quality < Xorriso_read_quality_valiD) {
     for(i= 0; i < lba_count; i++) {
       if(start_lbas[i] <= lba && end_lbas[i] >= lba) {
         start_byte= (lba - start_lbas[i]) * (off_t) 2048 + file_base_bytes;
     break;
       }
       file_base_bytes+= ((off_t) (end_lbas[i] + 1 - start_lbas[i]))
                          * (off_t) 2048;
     }
     if(i < lba_count) {
       sprintf(xorriso->info_text, "Bad extract  : %14.f , %14.f , %s\n",
               (double) start_byte, ((double) blocks) * 2048.0,
               Text_shellsafe(disk_path, sfe, 0));
       Xorriso_info(xorriso, 0);
       bad_extract= 1;
     }
   }
 }

 ret= !bad_extract;
ex:;
 if(start_lbas != NULL)
   free((char *) start_lbas);
 if(end_lbas != NULL)
   free((char *) end_lbas);
 Spotlist_destroy(&spotlist, 0);
 Checkmediajob_destroy(&job, 0);
 return(ret);
}


int Xorriso_extract_cut(struct XorrisO *xorriso,
                        char *img_path, char *disk_path,
                        off_t img_offset, off_t bytes, int flag)
{
 int ret, stbuf_ret, read_raw;
 double mem_lut= 0.0;
 char eff_img_path[SfileadrL], eff_disk_path[SfileadrL], sfe[SfileadrL*5];
 IsoImage *volume;
 IsoNode *node;

 ret= Xorriso_get_volume(xorriso, &volume, 0); 
 if(ret<=0)
   return(ret);
 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdi,
                                 img_path, eff_img_path, 0);
 if(ret<=0)
   return(ret);
 ret= Xorriso_node_from_path(xorriso, volume, eff_img_path, &node, 0);
 if(ret<=0)
   return(ret);
 ret= Xorriso_normalize_img_path(xorriso, xorriso->wdx,
                                 disk_path, eff_disk_path, 2 | 4);
 if(ret<=0)
   return(ret);
 Xorriso_pacifier_reset(xorriso, 0);
 mem_lut= xorriso->last_update_time;

 ret= Xorriso_handle_collision(xorriso, node, img_path, eff_disk_path,
                               disk_path, &stbuf_ret, 0);
 if(ret<=0 || ret==3)
   return(0);

 /* If it is a non-filtered stream from the ISO image
    and img_offset is a multiple of 2048
    then use Xorriso_read_file_data() for random access offset.
 */
 if(!LIBISO_ISREG(node)) {
   Xorriso_msgs_submit(xorriso, 0, eff_disk_path, 0, "ERRFILE", 0);
   sprintf(xorriso->info_text, "-extract_cut: ISO file %s is not a data file",
           Text_shellsafe(eff_img_path, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }
 read_raw= 0;
 if((img_offset % 2048) == 0) {
   ret= Xorriso_is_plain_image_file(xorriso, node, "", 0);
   if(ret > 0)
     read_raw= 1;
 }
 if (read_raw) {
   ret= Xorriso_read_file_data(xorriso, node, eff_img_path, eff_disk_path,
                               img_offset, (off_t) 0, bytes, 0);
   if(ret<=0)
     return(ret);
 } else {
   ret= Xorriso_tree_restore_node(xorriso, node, eff_img_path, img_offset,
                                  eff_disk_path, (off_t) 0, bytes, 2 | 8);
   if(ret<=0)
     return(ret);
 }

 ret= Xorriso_restore_properties(xorriso, eff_disk_path, node, 0);
 if(ret<=0)
   return(ret);

 if(mem_lut != xorriso->last_update_time)
   Xorriso_pacifier_callback(xorriso, "sectors examined",
                             xorriso->pacifier_count, 0, "", 1);
 return(1);
}


int Xorriso_get_local_charset(struct XorrisO *xorriso, char **name, int flag)
{
 (*name)= iso_get_local_charset(0);
 return(1);
}


int Xorriso_set_local_charset(struct XorrisO *xorriso, char *name, int flag)
{
 int ret;
 char *nl_charset, sfe[5 * SfileadrL];
 iconv_t iconv_ret= (iconv_t) -1;

 nl_charset= nl_langinfo(CODESET);
 if(name == NULL)
   name= nl_charset;

 if(name != NULL) {
   iconv_ret= iconv_open(nl_charset, name);
   if(iconv_ret == (iconv_t) -1)
     goto cannot;
   else
     iconv_close(iconv_ret);
 }
 ret= iso_set_local_charset(name, 0);
 if(ret <= 0) {
cannot:;
   sprintf(xorriso->info_text,
           "-local_charset: Cannot assume as local character set: %s",
           Text_shellsafe(name, sfe, 0));
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   return(0);
 }
 sprintf(xorriso->info_text, "Local character set is now assumed as: %s",
         Text_shellsafe(name, sfe, 0));
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
 return(1);
}


int Xorriso_relax_compliance(struct XorrisO *xorriso, char *mode,
                                    int flag)
{
 char *npt, *cpt;
 int l, was;

 was= xorriso->relax_compliance;
 npt= cpt= mode;
 for(; npt!=NULL; cpt= npt+1) {
   npt= strchr(cpt,':');
   if(npt==NULL)
     l= strlen(cpt);
   else
     l= npt-cpt;
   if(l == 0)
 continue;
   if((l == 6 && strncmp(cpt, "strict", l) == 0) ||
      (l == 5 && strncmp(cpt, "clear", l) == 0)) {
     xorriso->relax_compliance= 0;
   } else if(l == 7 && strncmp(cpt, "default", l) == 0) {
     xorriso->relax_compliance= Xorriso_relax_compliance_defaulT;

   } else if((l == 12 && strncmp(cpt, "omit_version", l) == 0) ||
             (l == 15 && strncmp(cpt, "omit_version_on", l) == 0) ) {
     xorriso->relax_compliance|= isoburn_igopt_omit_version_numbers;
   } else if((l == 16 && strncmp(cpt, "omit_version_off", l) == 0)) {
     xorriso->relax_compliance&= ~isoburn_igopt_omit_version_numbers;

   } else if((l == 10 && strncmp(cpt, "deep_paths", l) == 0) ||
             (l == 13 && strncmp(cpt, "deep_paths_on", l) == 0)) {
     xorriso->relax_compliance|= isoburn_igopt_allow_deep_paths;
   } else if(l == 14 && strncmp(cpt, "deep_paths_off", l) == 0) {
     xorriso->relax_compliance&= ~isoburn_igopt_allow_deep_paths;

   } else if((l == 10 && strncmp(cpt, "long_paths", l) == 0) ||
             (l == 13 && strncmp(cpt, "long_paths_on", l) == 0) ) {
     xorriso->relax_compliance|= isoburn_igopt_allow_longer_paths;
   } else if(l == 14 && strncmp(cpt, "long_paths_off", l) == 0) {
     xorriso->relax_compliance&= ~isoburn_igopt_allow_longer_paths;

   } else if((l == 10 && strncmp(cpt, "long_names", l) == 0) ||
             (l == 13 && strncmp(cpt, "long_names_on", l) == 0)) {
     xorriso->relax_compliance|= isoburn_igopt_max_37_char_filenames;
   } else if(l == 14 && strncmp(cpt, "long_names_off", l) == 0) {
     xorriso->relax_compliance&= ~isoburn_igopt_max_37_char_filenames;

   } else if((l == 13 && strncmp(cpt, "no_force_dots", l) == 0) ||
             (l == 16 && strncmp(cpt, "no_force_dots_on", l) == 0)) {
     xorriso->relax_compliance|= isoburn_igopt_no_force_dots;
   } else if(l == 17 && strncmp(cpt, "no_force_dots_off", l) == 0) {
     xorriso->relax_compliance&= ~isoburn_igopt_no_force_dots;

   } else if((l ==  9 && strncmp(cpt, "lowercase", l) == 0) ||
             (l == 12 && strncmp(cpt, "lowercase_on", l) == 0)) {
     xorriso->relax_compliance|= isoburn_igopt_allow_lowercase;
   } else if(l == 13 && strncmp(cpt, "lowercase_off", l) == 0) {
     xorriso->relax_compliance&= ~isoburn_igopt_allow_lowercase;

   } else if((l == 10 && strncmp(cpt, "full_ascii", l) == 0) ||
             (l == 13 && strncmp(cpt, "full_ascii_on", l) == 0)) {
     xorriso->relax_compliance|= isoburn_igopt_allow_full_ascii;
   } else if(l == 14 && strncmp(cpt, "full_ascii_off", l) == 0) {
     xorriso->relax_compliance&= ~isoburn_igopt_allow_full_ascii;

   } else if((l == 17 && strncmp(cpt, "joliet_long_paths", l) == 0) ||
             (l == 20 && strncmp(cpt, "joliet_long_paths_on", l) == 0)) {
     xorriso->relax_compliance|= isoburn_igopt_joliet_longer_paths;
   } else if(l == 21 && strncmp(cpt, "joliet_long_paths_off", l) == 0) {
     xorriso->relax_compliance&= ~isoburn_igopt_joliet_longer_paths;

   } else if((l == 10 && strncmp(cpt, "always_gmt", l) == 0) ||
             (l == 13 && strncmp(cpt, "always_gmt_on", l) == 0)) {
     xorriso->relax_compliance|= isoburn_igopt_always_gmt;
   } else if(l == 14 && strncmp(cpt, "always_gmt_off", l) == 0) {
     xorriso->relax_compliance&= ~isoburn_igopt_always_gmt;

   } else if((l ==  9 && strncmp(cpt, "rec_mtime", l) == 0) ||
             (l == 12 && strncmp(cpt, "rec_mtime_on", l) == 0)) {
     xorriso->relax_compliance|= isoburn_igopt_dir_rec_mtime;
   } else if(l == 13 && strncmp(cpt, "rec_mtime_off", l) == 0) {
     xorriso->relax_compliance&= ~isoburn_igopt_dir_rec_mtime;

   } else if((l ==  6 && strncmp(cpt, "old_rr", l) == 0) ||
             (l ==  9 && strncmp(cpt, "old_rr_on", l) == 0) ||
             (l == 10 && strncmp(cpt, "new_rr_off", l) == 0)) {
     xorriso->relax_compliance|=
                isoburn_igopt_rrip_version_1_10 | isoburn_igopt_aaip_susp_1_10;
   } else if((l == 10 && strncmp(cpt, "old_rr_off", l) == 0) ||
             (l ==  9 && strncmp(cpt, "new_rr_on", l) == 0) ||
             (l ==  6 && strncmp(cpt, "new_rr", l) == 0)) {
     xorriso->relax_compliance&=
             ~(isoburn_igopt_rrip_version_1_10 | isoburn_igopt_aaip_susp_1_10);

   } else if((l == 14 && strncmp(cpt, "aaip_susp_1_10", l) == 0) ||
             (l == 17 && strncmp(cpt, "aaip_susp_1_10_on", l) == 0) ||
             (l == 18 && strncmp(cpt, "aaip_susp_1_12_off", l) == 0)) {
     xorriso->relax_compliance|= isoburn_igopt_aaip_susp_1_10;
   } else if((l == 18 && strncmp(cpt, "aaip_susp_1_10_off", l) == 0) ||
             (l == 17 && strncmp(cpt, "aaip_susp_1_12_on", l) == 0) ||
             (l == 14 && strncmp(cpt, "aaip_susp_1_12", l) == 0)) {
     xorriso->relax_compliance&= ~isoburn_igopt_aaip_susp_1_10;

   } else if((l ==  8 && strncmp(cpt, "iso_9660", l) == 0) ||
             (l == 11 && strncmp(cpt, "iso_9660_on", l) == 0)) {
     /* may have a meaning in future */;
   } else if(l == 12 && strncmp(cpt, "iso_9660_off", l) == 0) {
     /* may have a meaning in future */;
     Xorriso_msgs_submit(xorriso, 0,
            "-compliance -iso_9660_off : Cannot do anything else but ISO 9660",
            0, "FAILURE", 0);

   } else {
     if(l<SfileadrL)
       sprintf(xorriso->info_text, "-compliance: unknown rule '%s'",
               cpt);
     else
       sprintf(xorriso->info_text,
               "-compliance: oversized rule parameter (%d)", l);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     xorriso->relax_compliance= was;
     return(0);
   }
 }
 return(1);
}


/* @return 1=ok  2=ok, is default setting */
int Xorriso_get_relax_text(struct XorrisO *xorriso, char mode[1024],
                           int flag)
{
 int r;

 r= xorriso->relax_compliance;
 if(r == 0) {
   strcpy(mode, "strict");
   return(1);
 }
 strcpy(mode, "clear");
 if(r & isoburn_igopt_omit_version_numbers)
   strcat(mode, ":omit_version");
 if(r & isoburn_igopt_allow_deep_paths)
   strcat(mode, ":deep_paths");
 if(r & isoburn_igopt_allow_longer_paths)
   strcat(mode, ":long_paths");
 if(r & isoburn_igopt_max_37_char_filenames)
   strcat(mode, ":long_names");
 if(r & isoburn_igopt_no_force_dots)
   strcat(mode, ":no_force_dots");
 if(r & isoburn_igopt_allow_lowercase)
   strcat(mode, ":lowercase");
 if(r & isoburn_igopt_allow_full_ascii)
   strcat(mode, ":full_ascii");
 if(r & isoburn_igopt_joliet_longer_paths)
   strcat(mode, ":joliet_long_paths");
 if(r & isoburn_igopt_always_gmt)
   strcat(mode, ":always_gmt");
 if(r & isoburn_igopt_dir_rec_mtime)
   strcat(mode, ":rec_mtime");
 if(r & isoburn_igopt_rrip_version_1_10) {
   strcat(mode, ":old_rr");
   if(!(r & isoburn_igopt_aaip_susp_1_10))
     strcat(mode, ":aaip_susp_1_10_off");
 } else {
   strcat(mode, ":new_rr");
   if(r & isoburn_igopt_aaip_susp_1_10) 
     strcat(mode, ":aaip_susp_1_10");
 }

 return(1 + (r == Xorriso_relax_compliance_defaulT));
}


/**
    @param flag bit0= print mount command to result channel rather than
                      performing it 
                bit1= do not allow prefixes with cmd
                bit2= interpret unprefixed cmd as shell:
*/
int Xorriso_mount(struct XorrisO *xorriso, char *dev, int adr_mode,
                  char *adr_value, char *cmd, int flag)
{
 int ret, lba, track, session, params_flag= 0, is_safe= 0, is_extra_drive= 0;
 int give_up= 0, mount_chardev= 0, status;
 char volid[33], *devadr, mount_command[SfileadrL], adr_data[163], *adr_pt;
 char *dev_path, libburn_adr[BURN_DRIVE_ADR_LEN + SfileadrL];
 char sfe[5 * SfileadrL], *dpt, *sysname= "";
 struct stat stbuf;
 struct burn_drive_info *dinfo= NULL;
 struct burn_drive *drive= NULL;

 devadr= dev;
 adr_pt= adr_value;
 if(strcmp(dev, "indev") == 0) {
   ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                  "on attempt to perform -mount \"indev\"", 0);
   if(ret<=0)
     goto ex;
   dev_path= devadr= xorriso->indev;
   if(strncmp(dev_path, "stdio:", 6) == 0)
     dev_path+= 6;
   if(xorriso->in_drive_handle == xorriso->out_drive_handle)
     give_up= 3;
   else
     give_up= 1;
 } else if(strcmp(dev, "outdev") == 0) {
   ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                  "on attempt to perform -mount \"outdev\"", 
                                  2);
   if(ret<=0)
     goto ex;
   dev_path= devadr= xorriso->outdev;
   if(strncmp(dev_path, "stdio:", 6) == 0)
     dev_path+= 6;
   if(xorriso->in_drive_handle == xorriso->out_drive_handle)
     give_up= 3;
   else
     give_up= 2;
 } else {
   is_extra_drive= 1;
   dev_path= dev;
   if(strncmp(dev_path, "stdio:", 6) == 0)
     dev_path+= 6;

   /* do only accept regular files and block devices */
   ret= stat(dev_path, &stbuf);
   if(ret == -1) {
     sprintf(xorriso->info_text, "Cannot determine properties of file %s",
             Text_shellsafe(dev_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }
   ret= System_uname(&sysname, NULL, NULL, NULL, 0);
   if(ret > 0 && strcmp(sysname, "FreeBSD") == 0)
     mount_chardev= 1;
   if(!(S_ISREG(stbuf.st_mode) || (S_ISBLK(stbuf.st_mode) && !mount_chardev)
        || (S_ISCHR(stbuf.st_mode) && !mount_chardev))) {
     sprintf(xorriso->info_text,
             "File object is not suitable as mount device: %s",
             Text_shellsafe(dev_path, sfe, 0));
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }

   /* Aquire drive as direct libburn address or via stdio: prefix */
   ret= burn_drive_convert_fs_adr(dev, libburn_adr);
   Xorriso_process_msg_queues(xorriso,0);
   if(ret < 0)
     {ret= -1; goto ex;}
   if(ret == 0 && strncmp(dev, "stdio:", 6) != 0)
     sprintf(libburn_adr, "stdio:%s", dev);
   burn_preset_device_open(xorriso->drives_exclusive, 0, 0);
   ret= isoburn_drive_aquire(&dinfo, libburn_adr, 1);
   burn_preset_device_open(1, 0, 0);
   Xorriso_process_msg_queues(xorriso,0);
   if(ret <= 0)
     {ret= 0; goto ex;}
   drive= dinfo[0].drive;
 }

 if(adr_mode == 4 && strlen(adr_pt) <= 80) {
   ret= Xorriso__bourne_to_reg(adr_pt, adr_data, 0);
   if(ret == 1) {
     params_flag|= 4;
     adr_pt= adr_data;
   }
 }
 ret= isoburn_get_mount_params(drive, adr_mode, adr_pt, &lba, &track,
                               &session, volid, params_flag);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret <= 0)
   goto ex;
 if(session <= 0 || track <= 0 || ret == 2) {
   Xorriso_msgs_submit(xorriso, 0,
                "-mount : Given address does not point to an ISO 9660 session",
                0, "FAILURE", 0);
   ret= 0; goto ex;
 }
 if(strstr(devadr, "stdio:") == devadr)
   devadr+= 6;
 ret= Xorriso_make_mount_cmd(xorriso, cmd, lba, track, session, volid, devadr,
                             mount_command, flag & (2 | 4));
 if(ret <= 0)
   goto ex;
 if(ret == 2)
   is_safe= 1;

 if(is_extra_drive) {
   isoburn_drive_release(drive, 0);
   burn_drive_info_free(dinfo);
   drive= NULL;
 } else if(give_up > 0 && !((flag & 1) || (xorriso->mount_opts_flag & 1))) {
   Xorriso_give_up_drive(xorriso, give_up);
   if(ret <= 0)
     goto ex;
 }
 Xorriso_process_msg_queues(xorriso,0);

 sprintf(xorriso->info_text, "Volume id    : %s\n",
                             Text_shellsafe(volid, sfe, 0));
 Xorriso_info(xorriso, 0);
 if(flag & 1) {
   sprintf(xorriso->result_line, "%s\n", mount_command);
   Xorriso_result(xorriso,0);
 } else {
   sprintf(xorriso->info_text, "Mount command: %s\n", mount_command);
   Xorriso_info(xorriso, 0);
   if(!is_safe) {
     Xorriso_msgs_submit(xorriso, 0,
  "-mount : Will not perform mount command which stems from command template.",
       0, "SORRY", 0);
     sprintf(xorriso->result_line, "%s\n", mount_command);
     Xorriso_result(xorriso,0);
   } else {
     ret= Xorriso_execv(xorriso, mount_command, "/bin:/sbin", &status, 1);
     if(WIFEXITED(status) && WEXITSTATUS(status) != 0) {
       sprintf(xorriso->info_text,
               "-mount : mount command failed with exit value %d",
               (int) WEXITSTATUS(ret));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       ret= 0; goto ex;
     }
     sprintf(xorriso->info_text, "\nMounted session %d of device %s",
                                 session, Text_shellsafe(dev_path, sfe, 0));
     dpt= strchr(cmd, ':');
     if(dpt == NULL)
       dpt= cmd ;
     else
       dpt++;
     sprintf(xorriso->info_text + strlen(xorriso->info_text),
             " as directory %s\n", Text_shellsafe(dpt, sfe, 0));
     Xorriso_info(xorriso, 0);
   }
 }
 ret= 1;
ex:;
 if(is_extra_drive && drive != NULL) {
   isoburn_drive_release(drive, 0);
   burn_drive_info_free(dinfo);
   Xorriso_process_msg_queues(xorriso,0);
 }
 return(ret);
}


int Xorriso_auto_driveadr(struct XorrisO *xorriso, char *adr, char *result,
                          int flag)
{
 int ret, is_known_mmc= 0;
 char *path_pt, libburn_adr[BURN_DRIVE_ADR_LEN + SfileadrL];
 char *abs_pt, abs_adr[SfileadrL];

 path_pt= adr;
 if(strncmp(adr, "stdio:", 6) == 0)
   path_pt= adr + 6;
 else if(strncmp(adr, "mmc:", 4) == 0)
   path_pt= adr + 4;


 /* <<< replace by Xorriso_normalize_img_path() ? */;

 if(path_pt[0] != '/') {
   abs_pt= getcwd(abs_adr, SfileadrL - 1);
   if(abs_pt == NULL) {
     Xorriso_msgs_submit(xorriso, 0,
              "Relative drive path given. Cannot determine working directory.",
               errno, "FAILURE", 0);
     return(-1);
   }
   ret= Sfile_add_to_path(abs_adr, path_pt, 0);
   if(ret <= 0)
     return(-1);
 }

 is_known_mmc= burn_drive_convert_fs_adr(path_pt, libburn_adr);
 Xorriso_process_msg_queues(xorriso,0);

 ret= Xorriso_is_in_patternlist(xorriso, xorriso->drive_whitelist, path_pt, 0);
 if(ret > 0)
   goto ok;
 ret= Xorriso_is_in_patternlist(xorriso, xorriso->drive_blacklist, path_pt, 0);
 if(ret < 0)
   return(ret);
 if(ret) {
   strcpy(xorriso->info_text, "Drive address ");
   Text_shellsafe(adr, xorriso->info_text, 1);
   strcat(xorriso->info_text,
          " rejected because: -drive_class 'banned' ");
   Text_shellsafe(Xorriso_get_pattern(xorriso, xorriso->drive_blacklist,
                                      ret - 1, 0),
                  xorriso->info_text, 1);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }
 /* if in greylist and not MMC and not stdio prefix: reject */
 if(is_known_mmc < 0)
   return(ret);
 if(adr == path_pt && !is_known_mmc) { /* no prefix, no MMC */
   ret= Xorriso_is_in_patternlist(xorriso, xorriso->drive_greylist, path_pt,0);
   if(ret < 0)
     return(ret);
   if(ret) {
     strcpy(xorriso->info_text, "Drive address ");
     Text_shellsafe(adr, xorriso->info_text, 1);
     strcat(xorriso->info_text,
            " rejected because: not MMC and -drive_class 'risky' ");
     Text_shellsafe(Xorriso_get_pattern(xorriso,xorriso->drive_greylist,
                                        ret - 1, 0),
                    xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     Xorriso_msgs_submit(xorriso, 0,
                   "If the address is a legitimate target, prepend \"stdio:\"",
                   0, "HINT", 0);
     return(0);
   }
 }
ok:;
 if(strncmp(adr, "mmc:", 4) == 0) {
   if(Sfile_str(result, path_pt, 0) <= 0)
     return(0);
 } else if(adr == path_pt && is_known_mmc <= 0) {
   Sfile_str(result, "stdio:", 0);
   if(Sfile_str(result, adr, 1) <= 0)
     return(0);
 } else {
   if(Sfile_str(result, adr, 0) <= 0)
     return(0);
 }
 if(strncmp(result, "stdio:", 6)==0) {
   if(xorriso->ban_stdio_write) {
     strcpy(xorriso->info_text, "Drive address banned by -ban_stdio_write : ");
     Text_shellsafe(result, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     return(0);
   }
 }
 return(1);
}


/*
  @param flag bit0= do not remove leading slash
              bit1= append flatly to result_line and put out
*/
int Xorriso_getfname(struct XorrisO *xorriso, char *path, int flag)
{
 int ret, path_offset= 0, bsl_mem;
 char *bsl_path= NULL;

 if(path[0] == '/' && !(flag & 1))
   path_offset= 1;

 /* backslash escaped path rather than shellsafe path */
 ret= Sfile_bsl_encoder(&bsl_path, path + path_offset,
                        strlen(path + path_offset), 8);
 if(ret <= 0)
   return(-1);
 if(flag & 2) {
   sprintf(xorriso->result_line + strlen(xorriso->result_line),
           "%s\n", bsl_path[0] ? bsl_path : ".");
 } else {
   sprintf(xorriso->result_line, "# file: %s\n", bsl_path[0] ? bsl_path : ".");
 }
 free(bsl_path);
 bsl_path= NULL;
 /* temporarily disable -backslash_codes with result output */
 bsl_mem= xorriso->bsl_interpretation;
 xorriso->bsl_interpretation= 0;
 Xorriso_result(xorriso, 0);
 xorriso->bsl_interpretation= bsl_mem;
 return(1);
}


/* @param node      Opaque handle to IsoNode which is to be inquired instead of                     path if it is not NULL.
   @param path      is used as address if node is NULL.
   @param acl_text  if acl_text is not NULL, then *acl_text will be set to the
                    ACL text (without comments) of the file object. In this
                    case it finally has to be freed by the caller.
   @param flag      bit0= do not report to result but only retrieve ACL text
                    bit1= check for existence of true ACL (not fabricated),
                          do not allocate and set acl_text but return 1 or 2
                    bit2-3: what ALC to retrieve:
                          0= "access" and "default", mark "default:"
                          1= "access" only
                          2= "default" only, do not mark "default:"
                    bit4= get "access" ACL only if not trivial
   @return          2 ok, no ACL available, eventual *acl_text will be NULL
                    1 ok, ACL available, eventual *acl_text stems from malloc()
                  <=0 error
*/
int Xorriso_getfacl(struct XorrisO *xorriso, void *in_node, char *path,
                    char **acl_text, int flag)
{
 int ret, d_ret, result_len= 0, pass, what;
 IsoNode *node;
 char *text= NULL, *d_text= NULL, *cpt, *npt;
 uid_t uid;
 gid_t gid;
 struct passwd *pwd;
 struct group *grp;

 what= (flag >> 2) & 3;
 if(acl_text != NULL)
   *acl_text= NULL;

 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret<=0)
     goto ex;
 }
 ret= iso_node_get_acl_text(node, &text, &d_text, flag & 16);
 d_ret= (d_text != NULL);
 if(ret < 0 || d_ret < 0) {
   if(path != NULL && path[0] != 0) {
     strcpy(xorriso->info_text, "Error with obtaining ACL of ");
     Text_shellsafe(path, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   }
   ret= 0; goto ex;
 }
 if(flag & 2) {
   ret= 1 + (ret != 1 && d_ret == 0);
   goto ex;
 }
 if((ret == 0 || ret == 2) && d_ret == 0) {
   if(flag & 1) {
     ret= 1 + (ret == 0);
     goto ex;
   }
   strcpy(xorriso->info_text, "No ACL associated with ");
   Text_shellsafe(path, xorriso->info_text, 1);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "DEBUG", 0);
   if(ret == 0)
     {ret= 2; goto ex;}
 }

 if(!(flag & 1)) {
   ret= Xorriso_getfname(xorriso, path, 0);
   if(ret <= 0)
     goto ex;
   uid= iso_node_get_uid(node);
   pwd= getpwuid(uid);
   if(pwd == NULL)
     sprintf(xorriso->result_line, "# owner: %.f\n", (double) uid);
   else
     sprintf(xorriso->result_line, "# owner: %s\n", pwd->pw_name);
   Xorriso_result(xorriso, 0);
   gid= iso_node_get_gid(node);
   grp= getgrgid(gid);
   if(grp == NULL)
     sprintf(xorriso->result_line, "# group: %.f\n", (double) gid);
   else
     sprintf(xorriso->result_line, "# group: %s\n", grp->gr_name);
   Xorriso_result(xorriso, 0);
 }

 for(pass= 0; pass < 1 + (acl_text != NULL && !(flag & 2)); pass++) {
   if(pass) {
     *acl_text= calloc(result_len + 1, 1);
     if(*acl_text == NULL) {
       Xorriso_no_malloc_memory(xorriso, NULL, 0);
       ret= -1; goto ex;
     }
   }
   if(text != NULL && what <= 1) {
     for(npt= cpt= text; npt != NULL; cpt= npt + 1) {
       npt= strchr(cpt, '\n');
       if(npt != NULL)
         *npt= 0;
       if(*cpt == 0) {
         if(d_text != NULL || pass) {
           if(npt != NULL)
             *npt= '\n';
     continue;
         }
       } else
         result_len+= strlen(cpt) + 1;
       if(pass) {
         sprintf(*acl_text + strlen(*acl_text), "%s\n", cpt);
       } else if(!(flag & 1)) {
         Sfile_str(xorriso->result_line, cpt, 0);
         strcat(xorriso->result_line, "\n");
         Xorriso_result(xorriso, 0);
       }
       if(npt != NULL)
         *npt= '\n';
     }
   }
   if(d_text != NULL && (what == 0 || what == 2)) {
     for(npt= cpt= d_text; npt != NULL; cpt= npt + 1) {
       npt= strchr(cpt, '\n');
       if(npt != NULL)
         *npt= 0;
       if(*cpt != 0) {
         if(pass) {
           if(what == 0)
             sprintf(*acl_text + strlen(*acl_text), "default:%s\n", cpt);
           else
             sprintf(*acl_text + strlen(*acl_text), "%s\n", cpt);
         } else {
           xorriso->result_line[0]= 0;
           if(what == 0)
             Sfile_str(xorriso->result_line, "default:", 0);
           Sfile_str(xorriso->result_line, cpt, 1);
           result_len+= strlen(cpt) + 9;
         }
       } else
         xorriso->result_line[0]= 0;
       if(pass== 0 && !(flag & 1)) {
         strcat(xorriso->result_line, "\n");
         Xorriso_result(xorriso, 0);
       }
       if(npt != NULL)
         *npt= '\n';
     }
   }
 }
 ret= 1;
ex:;
 iso_node_get_acl_text(node, &text, &d_text, 1 << 15);
 return(ret);
}


int Xorriso_set_ignore_aclea(struct XorrisO *xorriso, int flag)
{
 int ret, hflag;
 IsoImage *volume;

 ret= Xorriso_get_volume(xorriso, &volume, 1); 
 if(ret<=0)
   return(ret);
 hflag= (~xorriso->do_aaip) & 1;
 if((xorriso->ino_behavior & (1 | 2)) && !(xorriso->do_aaip & (4 | 16)))
   hflag|= 2; 
 iso_image_set_ignore_aclea(volume, hflag);
 return(1);
}


/* @param node      Opaque handle to IsoNode which is to be manipulated
                    instead of path if it is not NULL.
   @param path      is used as address if node is NULL.
   @param access_text  "access" ACL in long text form
   @param default_text "default" ACL in long text form
   @param flag      bit0= do not warn of root directory if not capable of AAIP
   @return          >0 success , <=0 failure
*/
int Xorriso_setfacl(struct XorrisO *xorriso, void *in_node, char *path,
                    char *access_text, char *default_text, int flag)
{
 int ret;
 IsoNode *node;

 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret<=0)
     goto ex;
 }
 ret= iso_node_set_acl_text(node, access_text, default_text, 0);
 if(ret <= 0) {
   if(path != NULL && path[0] != 0) {
     strcpy(xorriso->info_text, "Error with setting ACL of ");
     Text_shellsafe(path, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   }
   ret= 0; goto ex;
 }
 Xorriso_set_change_pending(xorriso, 0);
 ret= 1;
ex:;
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


/*
   @param flag      bit0= do not report to result but only retrieve attr text
                    bit1= path is disk_path
                    bit3= do not ignore eventual non-user attributes.
                    bit5= in case of symbolic link on disk: inquire link target
                    bit6= check for existence of xattr, return 0 or 1
                          (depends also on bit3)
*/
int Xorriso_getfattr(struct XorrisO *xorriso, void *in_node, char *path,
                     char **attr_text, int flag)
{
 int ret= 1, i, bsl_mem, result_len= 0, pass;
 size_t num_attrs= 0, *value_lengths= NULL;
 char **names= NULL, **values= NULL, *bsl;

 if(attr_text != NULL)
   *attr_text= NULL;
 ret= Xorriso_get_attrs(xorriso, in_node, path, &num_attrs, &names,
                        &value_lengths, &values, flag & (2 | 8 | 32));
 if(ret <= 0)
   goto ex;
 if(flag & 64) {
   ret= (num_attrs > 0);
   goto ex;
 }
 if(num_attrs == 0)
   {ret= 2; goto ex;}

 if(!(flag & 1)) {
   ret= Xorriso_getfname(xorriso, path, 0);
   if(ret <= 0)
     goto ex;
 }
 for(pass= 0; pass < 1 + (attr_text != NULL); pass++) {
   if(pass) {
     *attr_text= calloc(result_len + 1, 1);
     if(*attr_text == NULL) {
       Xorriso_no_malloc_memory(xorriso, NULL, 0);
       ret= -1; goto ex;
     }
   }
   for(i= 0; i < num_attrs; i++) {
     if(strlen(names[i]) + value_lengths[i] >= SfileadrL) {
       sprintf(xorriso->result_line, "# oversized: name %d , value %d bytes\n",
               (int) strlen(names[i]), (int) value_lengths[i]);
     } else {
       ret= Sfile_bsl_encoder(&bsl, names[i], strlen(names[i]), 8);
       if(ret <= 0) 
         {ret= -1; goto ex;}
       strcpy(xorriso->result_line, bsl);
       free(bsl);
       ret= Sfile_bsl_encoder(&bsl, values[i], value_lengths[i], 8);
       if(ret <= 0) 
         {ret= -1; goto ex;}
       sprintf(xorriso->result_line + strlen(xorriso->result_line),
               "=\"%s\"\n", bsl);
       free(bsl);
     }
     /* temporarily disable -backslash_codes with result output */
     result_len+= strlen(xorriso->result_line);
     if(pass) {
       strcat(*attr_text, xorriso->result_line);
     } else if(!(flag & 1)) {
       bsl_mem= xorriso->bsl_interpretation;
       xorriso->bsl_interpretation= 0;
       Xorriso_result(xorriso, 0);
       xorriso->bsl_interpretation= bsl_mem;
     }
   }
 }
 if(!(flag & 1)) {
   strcpy(xorriso->result_line, "\n");
   Xorriso_result(xorriso, 0);
 }
 ret= 1;
ex:;
 Xorriso_get_attrs(xorriso, in_node, path, &num_attrs, &names,
                   &value_lengths, &values, 1 << 15);
 return(ret);
}


/* @param in_node       Opaque handle to IsoNode which is to be manipulated
                        instead of path if it is not NULL.
   @param path          is used as address if node is NULL.
   @param num_attrs     Number of attributes
   @param names         Array of pointers to 0 terminated name strings
   @param value_lengths Array of byte lengths for each attribute payload
   @param values        Array of pointers to the attribute payload bytes
   @param flag          bit0= Do not maintain eventual existing ACL of the node
                        bit1= Do not clear the existing attribute list
                        bit2= Delete the attributes with the given names
                        bit3= Allow non-user attributes.
                        bit4= do not warn of root if incapable of AAIP
   @return              >0 success , <=0 failure
*/
int Xorriso_setfattr(struct XorrisO *xorriso, void *in_node, char *path,
                     size_t num_attrs, char **names,
                     size_t *value_lengths, char **values, int flag)
{
 int ret;
 IsoNode *node;

 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret<=0)
     goto ex;
 }
 ret= iso_node_set_attrs(node, num_attrs, names, value_lengths, values,
                         flag & (1 | 2 | 4 | 8));
 Xorriso_process_msg_queues(xorriso,0);
 if(ret <= 0) {
   Xorriso_report_iso_error(xorriso, "", ret,
                            "Error when setting ACL and xattr to image node",
                            0, "FAILURE", 1);
   if(path != NULL && path[0] != 0) {
     strcpy(xorriso->info_text, "Error with setting xattr of ");
     Text_shellsafe(path, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   }
   ret= 0; goto ex;
 }
 Xorriso_set_change_pending(xorriso, 0);
 ret= 1;
ex:;
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


/*
 @param flag
       Bitfield for control purposes
            bit0=  get default ACL rather than access ACL
            bit4=  set *text = NULL and return 2
                   if the ACL matches st_mode permissions.
            bit5=  in case of symbolic link: inquire link target
            bit15= free text and return 1
 @return
       1 ok
       2 ok, trivial ACL found while bit4 is set, *text is NULL
       0 no ACL manipulation adapter available / ACL not supported by fs
      -1 failure of system ACL service (see errno)
      -2 attempt to inquire ACL of a symbolic link without bit4 or bit5
         resp. with no suitable link target
*/
int Xorriso_local_getfacl(struct XorrisO *xorriso, char *disk_path,
                          char **text, int flag)
{
 int ret, skip= 0, colons= 0, countdown= 0;
 char *acl= NULL, *cpt, *wpt;

 if(flag & (1 << 15)) {
   if(*text != NULL)
     free(*text);
   *text= NULL;
   return(1);
 }
 *text= NULL;
 ret= iso_local_get_acl_text(disk_path, &acl, flag & (1 | 16 | 32));
 Xorriso_process_msg_queues(xorriso,0);
 if(ret < 0 || ret == 2)
   return(ret);
 if(acl == NULL)
   return(0);
 *text= strdup(acl);
 iso_local_get_acl_text(disk_path, &acl, 1 << 15);
 if(*text == NULL) {
   Xorriso_no_malloc_memory(xorriso, NULL, 0);
   return(-1);
 }

 /* Garbage collection about trailing remarks after 3 permission chars */
 wpt= *text;
 for(cpt= *text; *cpt; cpt++) {
   if(skip) {
     if(*cpt == '\n')
       skip= 0;
     else
 continue;
   }
   if(*cpt == ':' && !countdown) {
     colons++;
     if(colons == 2) {
       countdown= 4;
       colons= 0;
     }
   }
   if(countdown > 0) {
     countdown--;
     if(countdown == 0)
       skip= 1;
   }
   *wpt= *cpt;
   wpt++;
 }
 *wpt= 0;

 return(1);
}


/*
   @param flag
               bit1= path is disk_path
               bit3= do not ignore eventual non-user attributes.
               bit5= in case of symbolic link on disk: inquire link target
              bit15= free memory
*/
int Xorriso_get_attrs(struct XorrisO *xorriso, void *in_node, char *path,
                      size_t *num_attrs, char ***names,
                      size_t **value_lengths, char ***values, int flag)
{
 int ret, i, widx;
 IsoNode *node;

 if(flag & (1 << 15)) {
   if(flag & 2) {
     iso_local_get_attrs(NULL, num_attrs, names, value_lengths, values,
                         1 << 15);
   } else {
     iso_node_get_attrs(NULL, num_attrs, names, value_lengths, values,
                        1 << 15);
   }
   return(1);
 }

 *num_attrs= 0;
 if(flag & 2) {
   ret= iso_local_get_attrs(path, num_attrs, names, value_lengths, values,
                            flag & (8 | 32));
   if(ret < 0) {
     strcpy(xorriso->info_text, "Error with reading xattr of disk file ");
     Text_shellsafe(path, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE", 0);
   }
 } else {
   node= (IsoNode *) in_node;
   if(node == NULL) {
     ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
     if(ret<=0)
       goto ex;
   }
   ret= iso_node_get_attrs(node, num_attrs, names, value_lengths, values,
                           0);
   if(ret < 0) {
     Xorriso_report_iso_error(xorriso, "", ret,
                    "Error when obtaining xattr of ISO node", 0, "FAILURE", 1);
     goto ex;
   }

   if(!(flag & 8)) {
     /* Filter away any non-userspace xattr */;
     widx= 0;
     for(i= 0; i < *num_attrs; i++) {
       if(strncmp((*names)[i], "user.", 5) != 0) {
         free((*names)[i]);
         (*names)[i]= NULL;
         if((*values)[i] != NULL) {
           free((*values)[i]);
           (*values)[i]= NULL;
         }
       } else {
         if(widx != i) {
           (*names)[widx]= (*names)[i];
           (*value_lengths)[widx]= (*value_lengths)[i];
           (*values)[widx]= (*values)[i];
           (*names)[i]= NULL;
           (*value_lengths)[i]= 0;
           (*values)[i]= NULL;
         }
         widx++;
       }
     }
     *num_attrs= widx;
   }
 }
 ret= 1;
ex:;
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


int Xorriso_get_attr_value(struct XorrisO *xorriso, void *in_node, char *path,
                      char *name, size_t *value_length, char **value, int flag)
{
 int ret;
 size_t num_attrs= 0, *value_lengths= NULL, i;
 char **names = NULL, **values= NULL;

 *value= NULL;
 *value_length= 0;
 ret= Xorriso_get_attrs(xorriso, in_node, path, &num_attrs, &names,
                        &value_lengths, &values, 8);
 if(ret <= 0)
   goto ex;

 for(i= 0; i < num_attrs; i++) {
   if(strcmp(name, names[i]) != 0)
 continue;
   *value= calloc(value_lengths[i] + 1, 1);
   if(*value == NULL)
     {ret= -1; goto ex;}
   memcpy(*value, values[i], value_lengths[i]);
   (*value)[value_lengths[i]]= 0;
   *value_length= value_lengths[i];
   ret= 1; goto ex;
 }
 ret= 0;
ex:
 Xorriso_get_attrs(xorriso, in_node, path, &num_attrs, &names,
                        &value_lengths, &values, 1 << 15);
 return(ret);
}


/*
   @param flag bit0= use parameters dev,ino rather than disk_path
               bit1= compare attribute rather than setting it
                     return: 0=dev,ino match, 1=mismatch, 2=no node attribute
                            -1=error
               bit5= if not bit0:
                     transfer dev,inode from eventual link target
               bit7= omit dev check mit bit1
*/
int Xorriso_record_dev_inode(struct XorrisO *xorriso, char *disk_path,
                             dev_t dev, ino_t ino,
                             void *in_node, char *iso_path, int flag)
{
 size_t l, di_l= 0;
 int i, ret;
 dev_t hdev;
 ino_t hino;
 char buf[66], *bufpt, *wpt, *di= NULL;
 static char *name= "isofs.di";
 struct stat stbuf;

 if(!(flag & 1)) {
   if(flag & 32) {
     if(stat(disk_path, &stbuf) == -1)
       return(-1);
   } else {
     if(lstat(disk_path, &stbuf) == -1)
       return(-1);
   }
   dev= stbuf.st_dev;
   ino= stbuf.st_ino;
 }
   
 wpt= buf;
 hdev= dev;
 for(i= 0; hdev != 0; i++)
   hdev= hdev >> 8;
 l= i;
 *(wpt++)= l;
 for(i= 0; i < l; i++)
   *(wpt++)= dev >> (8 * (l - i - 1));
 hino= ino;
 for(i= 0; hino != 0; i++)
   hino= hino >> 8;
 l= i;
 *(wpt++)= l;
 for(i= 0; i < l; i++)
   *(wpt++)= ino >> (8 * (l - i - 1));
 l= wpt - buf; 
 bufpt= buf;

 if(flag & 2) {
   /* Compare node attribute with bufpt,l */
   ret= Xorriso_get_attr_value(xorriso, in_node, iso_path,
                               "isofs.di", &di_l, &di, 0);
   if(ret < 0)
     goto ex;
   if(ret == 0)
     {ret= 2; goto ex;}
   if(flag & 128) {
     if(di_l <= 0)
       {ret= 1; goto ex;}
     hino= 0;
     for(i= di[0] + 2; i < di_l && i - di[0] - 2 < di[(int) di[0] + 1]; i++)
       hino= (hino << 8) | ((unsigned char *) di)[i];
     if(hino != ino)
       {ret= 1; goto ex;} 
   } else {
     if(l != di_l)
       {ret= 1; goto ex;}
     for(i= 0; i < l; i++)
       if(di[i] != buf[i])
         {ret= 1; goto ex;} 
   }
   ret= 0;
 } else {
   ret= Xorriso_setfattr(xorriso, in_node, iso_path,
                         (size_t) 1, &name, &l, &bufpt, 2 | 8);
 }
ex:;
 if(di != NULL)
   free(di);
 return(ret);
}


struct Xorriso_extF {

 int flag; /* unused yet */

 IsoExternalFilterCommand *cmd;

};

int Xorriso_extf_destroy(struct XorrisO *xorriso, struct Xorriso_extF **filter,
                         int flag);


/* @param flag see struct Xorriso_extF.flag */
int Xorriso_extf_new(struct XorrisO *xorriso, struct Xorriso_extF **filter,
                     char *path, int argc, char **argv, int behavior,
                     char *suffix, char *name, int flag)
{
 int i;
 struct Xorriso_extF *o= NULL;
 IsoExternalFilterCommand *cmd;

 *filter= o= calloc(sizeof(struct Xorriso_extF), 1);
 if(o == NULL)
   goto failure;
 o->flag= flag;
 o->cmd= NULL;
 o->cmd= cmd= calloc(sizeof(IsoExternalFilterCommand), 1);
 if(cmd == NULL)
   goto failure;
 cmd->version= 0;
 cmd->refcount= 0;
 cmd->name= NULL;
 cmd->path= NULL;
 cmd->argv= NULL;
 cmd->argc= argc + 1;
 cmd->behavior= behavior;
 cmd->suffix= NULL;
 cmd->suffix= strdup(suffix);
 if(cmd->suffix == NULL)
   goto failure;

 cmd->path= strdup(path);
 if(cmd->path == NULL)
   goto failure;
 cmd->argv= calloc(sizeof(char *), argc + 2);
 if(cmd->argv == NULL)
   goto failure;
 for(i= 0; i < argc + 2; i++)
   cmd->argv[i]= NULL;
 cmd->argv[0]= strdup(path);
 if(cmd->argv[0] == NULL)
   goto failure;
 for(i= 0; i < argc; i++) {
   cmd->argv[i + 1]= strdup(argv[i]);
   if(cmd->argv[i] == NULL)
     goto failure;
 }
 cmd->name= strdup(name);
 if(cmd->name == NULL)
   goto failure;
 return(1);
failure:;
 Xorriso_extf_destroy(xorriso, filter, 0);
 return(-1);
}


int Xorriso_extf_destroy(struct XorrisO *xorriso, struct Xorriso_extF **filter,
                         int flag)
{
 int i;
 IsoExternalFilterCommand *cmd;

 if(*filter == NULL)
   return(0);
 cmd= (*filter)->cmd;
 if(cmd != NULL) {
   if(cmd->refcount > 0)
     return(0);
   if(cmd->suffix != NULL)
     free(cmd->suffix);
   if(cmd->argv != NULL) {
     for(i= 0; i < cmd->argc; i++)
       if(cmd->argv[i] != NULL)
         free(cmd->argv[i]);
     free((char *) cmd->argv);
   }
   if(cmd->name != NULL)
     free(cmd->name);
 }
 free((char *) *filter);
 *filter= NULL;
 return(1);
}


int Xorriso_lookup_extf(struct XorrisO *xorriso, char *name,
                        struct Xorriso_lsT **found_lst, int flag)
{
 struct Xorriso_extF *filter;
 struct Xorriso_lsT *lst;

 for(lst= xorriso->filters; lst != NULL; lst= Xorriso_lst_get_next(lst, 0)) {
   filter= (struct Xorriso_extF *) Xorriso_lst_get_text(lst, 0);
   if(strcmp(filter->cmd->name, name) == 0) {
     *found_lst= lst;
     return(1);
   }
 }
 return(0);
}


int Xorriso_destroy_all_extf(struct XorrisO *xorriso, int flag)
{
 struct Xorriso_extF *filter;
 struct Xorriso_lsT *lst, *next_lst;

 for(lst= xorriso->filters; lst != NULL; lst= next_lst) {
   filter= (struct Xorriso_extF *) Xorriso_lst_get_text(lst, 0);
   Xorriso_lst_detach_text(lst, 0);
   next_lst= Xorriso_lst_get_next(lst, 0);
   Xorriso_lst_destroy(&lst, 0);
   Xorriso_extf_destroy(xorriso, &filter, 0);
 }
 xorriso->filters= NULL;
 return(1);
}


/*
 @param flag   bit0= return 2 if renaming is not possible by libisofs
                     (always: if demanded strip suffix is missing
                              or if suffix makes name length > 255)
               bit1= strip suffix rather than appending it
*/
int Xorriso_rename_suffix(struct XorrisO *xorriso, IsoNode *node, char *suffix,
                          char *show_path, char new_name[], int flag)
{
 int ret, lo= 0, ls= 0, strip_suffix;
 char *old_name= NULL, *show_name;

 strip_suffix= !!(flag & 2);
 
 old_name= strdup((char *) iso_node_get_name(node));
 show_name= old_name;
 if(show_path != NULL)
   if(show_path[0] != 0)
     show_name= show_path;
 lo= strlen(old_name);
 ls= strlen(suffix);
 if(strip_suffix) {
   if(lo <= ls) {
     /* refuse gracefully */
     ret= 2; goto ex;
   }
   if(strcmp(old_name + lo - ls, suffix) != 0) {
     ret= 2; goto ex;
   }
   if(lo >= SfileadrL)
     goto cannot_remove_suffix;
   strcpy(new_name, old_name);
   new_name[lo - ls]= 0;
   ret = iso_node_set_name(node, new_name);
   if (ret < 0) {
     Xorriso_process_msg_queues(xorriso,0);
     if (!(flag & 1))
       Xorriso_report_iso_error(xorriso, "", ret,
                            "Error when renaming ISO node", 0, "FAILURE", 1);
cannot_remove_suffix:;
     strcpy(xorriso->info_text, "-set_filter: Cannot remove suffix from ");
     Text_shellsafe(show_name, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0,
                         (flag & 1) ? "WARNING" : "FAILURE", 0);
     ret= 2 * (flag & 1); goto ex;
   }
 } else {
   /* check whether suffix already present */
   if(lo >= ls)
     if(strcmp(old_name + lo - ls, suffix) == 0) {
       /* refuse gracefully */
       ret= 2; goto ex;
     }
   if(lo + ls > 255) {
cannot_append_suffix:;
     strcpy(xorriso->info_text, "-set_filter: Cannot append suffix to ");
     Text_shellsafe(show_name, xorriso->info_text, 1);
     strcat(xorriso->info_text, ". Left unfiltered.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0,
                         (flag & 1) ? "WARNING" : "FAILURE", 0);
     ret= 2 * (flag & 1); goto ex;
   } 
   sprintf(new_name, "%s%s", old_name, suffix);
   ret = iso_node_set_name(node, new_name);
   if (ret < 0) {
     Xorriso_process_msg_queues(xorriso,0);
     if (!(flag & 1))
       Xorriso_report_iso_error(xorriso, "", ret,
                              "Error when renaming ISO node", 0, "FAILURE", 1);
     goto cannot_append_suffix;
   }
 }

 ret= 1;
ex:;
 if(old_name != NULL)
   free(old_name);
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


/*
 @param flag   bit0= return 2 if renaming is not possible
               bit1= print pacifier messages
 */
int Xorriso_set_filter(struct XorrisO *xorriso, void *in_node,
                       char *path, char *filter_name, int flag)
{
 int ret, strip_suffix= 0, strip_filter= 0, filter_ret= 0;
 int explicit_suffix= 0, internal_filter= 0;
 IsoNode *node;
 IsoFile *file;
 struct Xorriso_lsT *found_lst;
 struct Xorriso_extF *found_filter;
 IsoExternalFilterCommand *cmd = NULL;
 char *old_name= NULL, new_name[SfileadrL], *suffix= "";
 IsoStream *stream;
 int is_renamed= 0;

 new_name[0]= 0;

 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret <= 0)
     goto ex;
 }
 if(!LIBISO_ISREG(node)) {
   strcpy(xorriso->info_text, "-set_filter: Not a regular data file node ");
   Text_shellsafe(path, xorriso->info_text, 1);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "SORRY", 0);
   ret= 0; goto ex;
 }
 file= (IsoFile *) node;

 if(strncmp(filter_name, "--remove-all-filters", 20) == 0) {
   strip_filter= 1;
   strip_suffix= 1;
   if(strlen(filter_name) > 21) {
     strip_suffix= (filter_name[20] != '+');
     suffix= filter_name + 21;
     explicit_suffix= 1;
   }
 } else if(strcmp(filter_name, "--zisofs") == 0) {
   internal_filter= 1;
 } else if(strcmp(filter_name, "--zisofs-decode") == 0) {
   internal_filter= 2;
 } else if(strcmp(filter_name, "--gzip") == 0) {
   internal_filter= 3;
   suffix= ".gz";
   strip_suffix= 0;
   explicit_suffix= 1;
 } else if(strcmp(filter_name, "--gunzip") == 0 ||
           strcmp(filter_name, "--gzip-decode") == 0) {
   internal_filter= 4;
   suffix= ".gz";
   strip_suffix= 1;
   explicit_suffix= 1;
 } else {
   ret= Xorriso_lookup_extf(xorriso, filter_name, &found_lst, 0);
   if(ret < 0)
     goto ex;
   if(ret == 0) {
     strcpy(xorriso->info_text, "-set_filter: Not a registered filter name ");
     Text_shellsafe(filter_name, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }
   found_filter= (struct Xorriso_extF *) Xorriso_lst_get_text(found_lst, 0);
   cmd= found_filter->cmd;
   suffix= cmd->suffix;
   strip_suffix= cmd->behavior & 8;
 }

 if(suffix[0]) {

   /* >>> would need full iso_rr_path of node for showing */;

   old_name= strdup((char *) iso_node_get_name(node));
   ret= Xorriso_rename_suffix(xorriso, node, suffix, path, new_name,
                               (flag & 1) | (strip_suffix ? 2 : 0));
   if(ret <= 0 || ret == 2)
     goto ex;
   is_renamed= 1;
 }

 if(strip_filter) {
   while(1) {
     if(!explicit_suffix) {
       stream= iso_file_get_stream(file);

       if(strncmp(stream->class->type, "gzip", 4) == 0) {
         suffix= ".gz";
         strip_suffix= 1;
       } else if(strncmp(stream->class->type, "pizg", 4) == 0) {
         suffix= ".gz";
         strip_suffix= 0;
       } else {
         ret= iso_stream_get_external_filter(stream, &cmd, 0);
         if(ret > 0) {
           suffix= cmd->suffix;
           strip_suffix= !(cmd->behavior & 8);
         }
       }
       if(suffix[0]) {

         /* >>> would need the current renaming state of path */;

         ret= Xorriso_rename_suffix(xorriso, node, suffix, NULL, new_name,
                                    (flag & 1) | (strip_suffix << 1));
         if(ret <= 0 || ret == 2)
           goto ex;
       }
     }
     ret= iso_file_remove_filter(file, 0);
     if(ret != 1)
   break;
   }
   filter_ret= 1;
 } else if (internal_filter == 1 || internal_filter == 2) {
   filter_ret = iso_file_add_zisofs_filter(file, 1 | (internal_filter & 2));
   if(filter_ret < 0) {
     Xorriso_process_msg_queues(xorriso,0);
     if(!(internal_filter == 2 && filter_ret == ISO_ZISOFS_WRONG_INPUT))
       Xorriso_report_iso_error(xorriso, "", filter_ret,
                    "Error when setting filter to ISO node", 0, "FAILURE", 1);
   }
 } else if (internal_filter == 3 || internal_filter == 4) {
   filter_ret = iso_file_add_gzip_filter(file,
                                         1 | ((internal_filter == 4) << 1));
   if(filter_ret < 0) {
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_report_iso_error(xorriso, "", filter_ret,
                    "Error when setting filter to ISO node", 0, "FAILURE", 1);
   }
 } else {

#ifndef Xorriso_allow_extf_suiD
   /* This is a final safety precaution before iso_file_add_external_filter()
      performs fork() and executes the alleged filter program.
   */
   if(getuid() != geteuid()) {
     sprintf(xorriso->info_text,
          "-set_filter: UID and EUID differ. Will not run external programs.");
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
     return(0);
   }
#endif /* ! Xorriso_allow_extf_suiD */

   filter_ret = iso_file_add_external_filter(file, cmd, 0);
   if(filter_ret < 0) {
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_report_iso_error(xorriso, "", filter_ret,
                    "Error when setting filter to ISO node", 0, "FAILURE", 1);
   }
 }
 if(filter_ret != 1 && new_name[0] && old_name != NULL) {
   ret = iso_node_set_name(node, old_name);
   if (ret < 0) {
     Xorriso_process_msg_queues(xorriso,0);
     if (!(flag & 1))
       Xorriso_report_iso_error(xorriso, "", ret,
                              "Error when renaming ISO node", 0, "FAILURE", 1);
   }
 }
 if(flag & 2) {
   xorriso->pacifier_count++;
   Xorriso_pacifier_callback(xorriso, "file filters processed",
                      xorriso->pacifier_count, xorriso->pacifier_total, "", 0);
 }
 if(filter_ret < 0) {
   ret= 0; goto ex;
 }

 ret= filter_ret;
ex:;
 if(old_name != NULL)
   free(old_name);
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


/* @param flag bit0= delete filter with the given name
*/
int Xorriso_external_filter(struct XorrisO *xorriso,
                            char *name, char *options, char *path, 
                            int argc, char **argv, int flag)
{
 int ret, delete= 0, behavior= 0, extf_flag= 0, is_banned= 0;
 char *what, *what_next, *suffix= "";
 struct Xorriso_lsT *lst;
 struct Xorriso_extF *found_filter, *new_filter= NULL;

#ifndef Xorriso_allow_external_filterS
 /* To be controlled by: configure --enable-external-filters */

 sprintf(xorriso->info_text, "%s : Banned at compile time.",
         flag & 1 ? "-unregister_filter" : "-external_filter");
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
 sprintf(xorriso->info_text,
"This may be changed at compile time by ./configure option --enable-external-filters");
 Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0);
 is_banned= 1;
 
#endif /* ! Xorriso_allow_external_filterS */

#ifndef Xorriso_allow_extf_suiD
 /* To be controlled by: configure --enable-external-filters-setuid */

 if(getuid() != geteuid()) {
   sprintf(xorriso->info_text,
          "-set_filter: UID and EUID differ. Will not run external programs.");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   sprintf(xorriso->info_text,
"This may be changed at compile time by ./configure option --enable-external-filters-setuid");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "HINT", 0);
   is_banned= 1;
 }
#endif /* ! Xorriso_allow_extf_suiD */

 if(is_banned)
   return(0);

 if(xorriso->filter_list_closed) {
   sprintf(xorriso->info_text,
           "%s : Banned by previous command -close_filter_list",
           flag & 1 ? "-unregister_filter" : "-external_filter");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 } 
 if((!(flag & 1)) && path[0] != '/') {
   sprintf(xorriso->info_text,
           "-external_filter : Given command path does not begin by '/' : ");
   Text_shellsafe(path, xorriso->info_text, 1);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   return(0);
 }


 delete= flag & 1;
 ret= Xorriso_lookup_extf(xorriso, name, &lst, 0);
 if(ret < 0)
   return(ret);
 if(ret > 0) {
   if(delete) {
     found_filter= (struct Xorriso_extF *) Xorriso_lst_get_text(lst, 0);
     if(found_filter->cmd->refcount > 0) {
       sprintf(xorriso->info_text,
 "-external_filter: Cannot remove filter because it is in use by %.f nodes : ",
              (double) found_filter->cmd->refcount);
       Text_shellsafe(name, xorriso->info_text, 1);
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
       ret= 0; goto ex;
     }
     Xorriso_lst_detach_text(lst, 0);
     if(xorriso->filters == lst)
       xorriso->filters= Xorriso_lst_get_next(lst, 0);
     Xorriso_lst_destroy(&lst, 0);
     Xorriso_extf_destroy(xorriso, &found_filter, 0);
     ret= 1; goto ex;
   }
   strcpy(xorriso->info_text,
          "-external_filter: filter with given name already existing: ");
   Text_shellsafe(name, xorriso->info_text, 1);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }
 if(delete) {
   strcpy(xorriso->info_text,
          "-external_filter: filter with given name does not exist: ");
   Text_shellsafe(name, xorriso->info_text, 1);
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   ret= 0; goto ex;
 }

 for(what= options; what!=NULL; what= what_next) {
   what_next= strchr(what, ':');
   if(what_next!=NULL) {
     *what_next= 0;
     what_next++;
   }
   if(strncmp(what, "default", 7) == 0) {
     suffix= "";
     behavior= 0;
   } else if(strncmp(what, "suffix=", 7) == 0) {
     suffix= what + 7;
   } else if(strcmp(what, "remove_suffix") == 0) {
     behavior|= 8;
   } else if(strcmp(what, "if_nonempty") == 0) {
     behavior|= 1;
   } else if(strcmp(what, "if_reduction") == 0) {
     behavior|= 2;
   } else if(strcmp(what, "if_block_reduction") == 0) {
     behavior|= 4;
   } else if(strncmp(what, "used=", 5) == 0) {
     ; /* this is informational output from -status */
   } else if(what[0]) {
     strcpy(xorriso->info_text,
            "-external_filter: unknown option ");
     Text_shellsafe(what, xorriso->info_text, 1);
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
     ret= 0; goto ex;
   }
 }

 ret= Xorriso_extf_new(xorriso, &new_filter, path, argc, argv, behavior,
                       suffix, name, extf_flag);
 if(ret <= 0) {
could_not_create:;
   strcpy(xorriso->info_text,
          "-external_filter: Could not create filter object");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FAILURE", 0);
   goto ex;
 }
 ret= Xorriso_lst_append_binary(&(xorriso->filters), (char *) new_filter,0, 4);
 if(ret <= 0)
   goto could_not_create;
 ret= 1;
ex:;
 if(ret <= 0) {
   if(new_filter != NULL)
     Xorriso_extf_destroy(xorriso, &new_filter, 0);
 }
 return(ret);
}


int Xorriso_status_extf(struct XorrisO *xorriso, char *filter, FILE *fp,
                        int flag)
/*
 bit1= do only report to fp
*/ 
{
 int i, maxl= 4 * SfileadrL;
 struct Xorriso_extF *extf;
 struct Xorriso_lsT *lst;
 char *line;

 line= xorriso->result_line;
 for(lst= xorriso->filters; lst != NULL; lst= Xorriso_lst_get_next(lst, 0)) {
   extf= (struct Xorriso_extF *) Xorriso_lst_get_text(lst, 0);
   
   strcpy(xorriso->result_line, "-external_filter ");
   Text_shellsafe(extf->cmd->name, line, 1);
   if(strlen(line) > maxl)
 continue;
   strcat(line, " ");
   if(extf->cmd->suffix[0]) {
     strcat(line, "suffix=");
     Text_shellsafe(extf->cmd->suffix, line, 1);
     if(strlen(line) > maxl)
 continue;
     strcat(line, ":");
   }
   if(extf->cmd->behavior & 8)
     strcat(line, "remove_suffix:");
   if(extf->cmd->behavior & 1)
     strcat(line, "if_nonempty:");
   if(extf->cmd->behavior & 2)
     strcat(line, "if_reduction:");
   if(extf->cmd->behavior & 4)
     strcat(line, "if_block_reduction:");
   sprintf(line + strlen(line), "used=%.f ", (double) extf->cmd->refcount);
   if(strlen(line) > maxl)
 continue;
   Text_shellsafe(extf->cmd->path, line, 1);
   if(strlen(line) > maxl)
 continue;
   for(i= 1; i < extf->cmd->argc; i++) {
     strcat(line, " ");
     Text_shellsafe(extf->cmd->argv[i], line, 1);
     if(strlen(line) > maxl)
   break;
   }
   if(i < extf->cmd->argc)
 continue;
   strcat(line, " --\n");
   Xorriso_status_result(xorriso, filter, fp, flag&2);
 }
 if(xorriso->filter_list_closed) {
   strcpy(line, "-close_filter_list\n");
   Xorriso_status_result(xorriso, filter, fp, flag&2);
 }
 return(1);
}


int Xorriso_stream_type(struct XorrisO *xorriso, IsoNode *node,
                        IsoStream *stream, char type_text[], int flag)
{
 int ret, lba;
 char text[5];

 strncpy(text, stream->class->type, 4);
 text[4]= 0;
 if(strcmp(text, "fsrc") == 0) {
   ret= Xorriso__file_start_lba(node, &lba, 0);
   if(ret > 0 && lba > 0)
     strcpy(type_text, "image");
   else
     strcpy(type_text, "disk");
 } else if(strcmp(text, "ziso") == 0) {
   strcpy(type_text, "--zisofs");
 } else if(strcmp(text, "osiz") == 0) {
   strcpy(type_text, "--zisofs-decode");
 } else if(strcmp(text, "gzip") == 0) {
   strcpy(type_text, "--gzip");
 } else if(strcmp(text, "pizg") == 0) {
   strcpy(type_text, "--gunzip");
 } else if(strcmp(text, "cout") == 0 || strcmp(text, "boot") == 0 ||
           strcmp(text, "user") == 0 || strcmp(text, "extf") == 0) {
   strcpy(type_text, text);
 } else {
   Text_shellsafe(text, type_text, 0);
 }
 return(1);
} 

int Xorriso_show_stream(struct XorrisO *xorriso, void *in_node,
                        char *path, int flag)
{
 int ret;
 IsoNode *node;
 IsoFile *file;
 IsoStream *stream= NULL, *input_stream;
 IsoExternalFilterCommand *cmd;
 char type_text[16], *source_path= NULL;

 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret <= 0)
     goto ex;
 }
 if(!LIBISO_ISREG(node)) 
   {ret= 2; goto ex;}
 file= (IsoFile *) node;
 input_stream= iso_file_get_stream(file);
 Text_shellsafe(path, xorriso->result_line, 0);
 while(1) {
   stream= input_stream;
   input_stream= iso_stream_get_input_stream(stream, 0);
   if(input_stream == NULL)
 break;
   strcat(xorriso->result_line, " < ");
   Xorriso_stream_type(xorriso, node, stream, type_text, 0);
   strcat(xorriso->result_line, type_text);
   ret= iso_stream_get_external_filter(stream, &cmd, 0);
   if(ret < 0) {
     Xorriso_process_msg_queues(xorriso,0);
     Xorriso_report_iso_error(xorriso, "", ret,
               "Error when inquiring filter command of node", 0, "FAILURE", 1);
     ret= 0; goto ex;
   }
   if(ret > 0) {
     strcat(xorriso->result_line, ":");
     Text_shellsafe(cmd->name, xorriso->result_line, 1);
   }
   if(strlen(xorriso->result_line) > SfileadrL) {
     Xorriso_result(xorriso, 0);
     xorriso->result_line[0]= 0;
   }
 }
 strcat(xorriso->result_line, " < ");
 Xorriso_stream_type(xorriso, node, stream, type_text, 0);
 strcat(xorriso->result_line, type_text);

 source_path= iso_stream_get_source_path(stream, 0);
 if(source_path != NULL) {
   strcat(xorriso->result_line, ":");
   Text_shellsafe(source_path, xorriso->result_line, 1);
 }
 
 strcat(xorriso->result_line, "\n");
 Xorriso_result(xorriso, 0);
 ret= 1;
ex:;
 if(source_path != NULL)
   free(source_path);
 return(ret); 
}


int Xorriso_set_zisofs_params(struct XorrisO *xorriso, int flag)
{
 int ret;
 struct iso_zisofs_ctrl ctrl;

 ctrl.version= 0;
 ctrl.compression_level= xorriso->zlib_level;
 if(xorriso->zisofs_block_size == (1 << 16))
   ctrl.block_size_log2= 16;
 else if(xorriso->zisofs_block_size == (1 << 17))
   ctrl.block_size_log2= 17;
 else
   ctrl.block_size_log2= 15;
 ret= iso_zisofs_set_params(&ctrl, 0);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret < 0) {
   Xorriso_report_iso_error(xorriso, "", ret,
                      "Error when setting zisofs parameters", 0, "FAILURE", 1);
   return(0);
 }
 return(1);
}


int Xorriso_status_zisofs(struct XorrisO *xorriso, char *filter, FILE *fp,
                          int flag)
/*
 bit0= do only report non-default settings
 bit1= do only report to fp
*/ 
{
 off_t ziso_count= 0, osiz_count= 0;
 off_t gzip_count= 0, gunzip_count= 0;

 iso_zisofs_get_refcounts(&ziso_count, &osiz_count, 0);
 iso_gzip_get_refcounts(&gzip_count, &gunzip_count, 0);
 if((flag & 1) && xorriso->zlib_level == xorriso->zlib_level_default &&
    xorriso->zisofs_block_size == xorriso->zisofs_block_size_default &&
    xorriso->zisofs_by_magic == 0 &&
    ziso_count == 0 && osiz_count == 0 &&
    gzip_count == 0 && gunzip_count == 0) {
   if(filter == NULL)
     return(2);
   if(filter[0] == 0)
     return 2;
 }
 sprintf(xorriso->result_line,
     "-zisofs level=%d:block_size=%dk:by_magic=%s:ziso_used=%.f:osiz_used=%.f",
     xorriso->zlib_level, xorriso->zisofs_block_size / 1024,
     xorriso->zisofs_by_magic ? "on" : "off",
     (double) ziso_count, (double) osiz_count);
 sprintf(xorriso->result_line + strlen(xorriso->result_line),
         ":gzip_used=%.f:gunzip_used=%.f\n",
         (double) gzip_count, (double) gunzip_count);
 Xorriso_status_result(xorriso, filter, fp, flag&2);
 return(1);
}


int Xorriso_all_node_array(struct XorrisO *xorriso, int addon_nodes, int flag)
{  
 int ret;
 struct FindjoB *job= NULL;
 struct stat dir_stbuf;

 ret= Findjob_new(&job, "/", 0);
 if(ret<=0) {
   Xorriso_no_findjob(xorriso, "xorriso", 0);
   {ret= -1; goto ex;}
 }
 Findjob_set_action_target(job, 30, NULL, 0);
 Xorriso_destroy_node_array(xorriso, 0);
 ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/",
                    &dir_stbuf, 0, 0);
 if(ret <= 0)
   goto ex;
 ret= Xorriso_new_node_array(xorriso, xorriso->temp_mem_limit, addon_nodes, 0);
 if(ret <= 0)
   goto ex;
 Findjob_set_action_target(job, 31, NULL, 0);
 ret= Xorriso_findi(xorriso, job, NULL, (off_t) 0, NULL, "/",
                    &dir_stbuf, 0, 0);
 if(ret <= 0)
   goto ex;
 ret= 1;
ex:;
 Findjob_destroy(&job, 0);
 return(ret);
}


int Xorriso_remake_hln_array(struct XorrisO *xorriso, int flag)
{  
 int ret, addon_nodes= 0, i, old_count, old_pt, new_pt;
 IsoNode **old_nodes;
 char **old_targets;

 /* Count hln_targets of which the node has been deleted meanwhile */
 for(i= 0; i < xorriso->hln_count; i++) {
   if(xorriso->hln_targets[i] == NULL)
 continue;
   if(Xorriso_node_is_valid(xorriso, xorriso->hln_array[i], 0))
 continue;
   addon_nodes++;
 }
 ret= Xorriso_all_node_array(xorriso, addon_nodes, 0);
 if(ret <= 0)
   goto ex;
 if(addon_nodes > 0) {
   /* Transfer delete nodes with hln_target to node array */
   for(i= 0; i < xorriso->hln_count; i++) {
     if(xorriso->hln_targets[i] == NULL)
   continue;
     if(Xorriso_node_is_valid(xorriso, xorriso->hln_array[i], 0))
   continue;
     if(xorriso->node_counter < xorriso->node_array_size) {
       xorriso->node_array[xorriso->node_counter++]= xorriso->hln_array[i];
       iso_node_ref(xorriso->node_array[xorriso->node_counter - 1]);
     }
   }
 }

 Xorriso_sort_node_array(xorriso, 0);
 old_nodes= (IsoNode **) xorriso->hln_array;
 old_targets= (char **) xorriso->hln_targets;
 old_count= xorriso->hln_count;
 xorriso->hln_array= 0;
 xorriso->hln_targets= NULL;

 /* Transfer node_array to di_array without unrefering nodes */
 xorriso->hln_count= xorriso->node_counter;
 xorriso->hln_array= xorriso->node_array;
 xorriso->node_counter= 0;
 xorriso->node_array_size= 0;
 xorriso->node_array= NULL;

 /* Allocate hln_targets */
 ret= Xorriso_new_hln_array(xorriso, xorriso->temp_mem_limit, 1);
 if(ret<=0)
   goto ex;
 xorriso->node_targets_availmem= xorriso->temp_mem_limit;
 if(old_targets != NULL) {
   /* Transfer targets from old target array */;
   new_pt= old_pt= 0;
   while(new_pt < xorriso->hln_count && old_pt < old_count) {
     ret= Xorriso__hln_cmp(xorriso->hln_array[new_pt], old_nodes[old_pt]);
     if(ret < 0) {
       new_pt++;
     } else if(ret > 0) {
       old_pt++;
     } else {
       xorriso->hln_targets[new_pt]= old_targets[old_pt];
       if(old_targets[old_pt] != NULL)
         xorriso->temp_mem_limit-= strlen(old_targets[old_pt]) + 1;
       old_targets[old_pt]= NULL;
       new_pt++;
       old_pt++;
     }
   }
   for(old_pt= 0; old_pt < old_count; old_pt++)
     if(old_targets[old_pt] != NULL) /* (should not happen) */
       free(old_targets[old_pt]);
   free((char *) old_targets);
 }
 if(old_nodes != NULL) {
   for(old_pt= 0; old_pt < old_count; old_pt++)
     if(old_nodes[old_pt] != NULL)
       iso_node_unref(old_nodes[old_pt]);
   free((char *) old_nodes);
 }
 xorriso->hln_change_pending= 0;
 ret= 1;
ex:;
 return(ret);
}


/* @param flag bit0= overwrite existing hln_array (else return 2)
*/
int Xorriso_make_hln_array(struct XorrisO *xorriso, int flag)
{  
 int ret;

 if(xorriso->hln_array != NULL && !(flag & 1)) {
   /* If no fresh image manipulations occured: keep old array */
   if(!xorriso->hln_change_pending)
     return(2);
   ret= Xorriso_remake_hln_array(xorriso, 0);
   return(ret);
 }
 Xorriso_destroy_hln_array(xorriso, 0);

 ret= Xorriso_all_node_array(xorriso, 0, 0);
 if(ret <= 0)
   goto ex;
 Xorriso_sort_node_array(xorriso, 0);

 /* Transfer node_array to di_array without unrefering nodes */
 xorriso->hln_count= xorriso->node_counter;
 xorriso->hln_array= xorriso->node_array;
 xorriso->node_counter= 0;
 xorriso->node_array_size= 0;
 xorriso->node_array= NULL;

 /* Allocate hln_targets */
 ret= Xorriso_new_hln_array(xorriso, xorriso->temp_mem_limit, 1);
 if(ret<=0) {
   Xorriso_destroy_hln_array(xorriso, 0);
   goto ex;
 }
 xorriso->node_targets_availmem= xorriso->temp_mem_limit;
 xorriso->hln_change_pending= 0;
 ret= 1;
ex:;
 return(ret);
}


/* @param flag bit0= overwrite existing di_array (else return 2)
               bit1= make di_array despite xorriso->ino_behavior bit 3
*/
int Xorriso_make_di_array(struct XorrisO *xorriso, int flag)
{  
 int ret, bytes; 

#ifdef NIX
 /* <<< */
 unsigned long old_gdic;
 old_gdic= Xorriso_get_di_counteR;
#endif /* NIX */

 if((xorriso->ino_behavior & 8 ) && !(flag & 2))
   return(2);
 if(xorriso->di_array != NULL && !(flag & 1))
   return(2);
 Xorriso_finish_hl_update(xorriso, 0);

 ret= Xorriso_all_node_array(xorriso, 0, 0);
 if(ret <= 0)
   goto ex;
 bytes= xorriso->node_array_size / 8 + 1;
 xorriso->di_do_widen= calloc(bytes, 1);
 if(xorriso->di_do_widen == NULL) {
   Xorriso_no_malloc_memory(xorriso, NULL, 0);
   ret= -1; goto ex;
 }

 /* Transfer node_array to di_array without unrefering nodes */
 xorriso->di_count= xorriso->node_counter;
 xorriso->di_array= xorriso->node_array;
 xorriso->node_counter= 0;
 xorriso->node_array_size= 0;
 xorriso->node_array= NULL;

 Xorriso__sort_di((void *) xorriso->di_array, xorriso->di_count, 0);

 ret= 1;
ex:;

#ifdef NIX
/* <<< */
 fprintf(stderr, "xorriso_DEBUG: sort_count= %lu\n",
         Xorriso_get_di_counteR - old_gdic);
#endif /* NIX */

 return(ret);
}


/*
   @param flag  bit0= iso_rr_path is freshly added and up to date
                bit2= -follow: this is not a command parameter
   @return -1= severe error
            0= not applicable for hard links
            1= go on with processing
            2= iso_rr_path is fully updated
 */
int Xorriso_hardlink_update(struct XorrisO *xorriso, int *compare_result,
                            char *disk_path, char *iso_rr_path, int flag)
{
 int ret, hret, idx, low, high, i, do_overwrite= 0, did_fake_di= 0;
 int follow_links, old_idx= -1;
 IsoNode *node;
 struct stat stbuf;
 dev_t old_dev;
 ino_t old_ino;

 if(xorriso->di_array == NULL)
   return(1);
 follow_links= xorriso->do_follow_links ||
               (xorriso->do_follow_param && !(flag & 4));
 ret= Xorriso_node_from_path(xorriso, NULL, iso_rr_path, &node, 0);
 if(ret <= 0)
   return(ret);
 if(LIBISO_ISDIR(node))
   return(1);

 /* Handle eventual hardlink split : */
 /* This is achieved by setting the content change bit. Reason:
    The node needs to be removed from di_array because its di is
    not matching its array index any more. So it becomes invisible for
    the join check of eventual later hardlink siblings. Therefore
    it must be updated now, even if it has currently no siblings
    which it leaves or which it joins.
 */
 if(!(flag & 1))
   do_overwrite= 1;

 Xorriso__get_di(node, &old_dev, &old_ino, 0);
 ret= Xorriso__search_node(xorriso->di_array, xorriso->di_count,
                           Xorriso__di_cmp, node, &idx, 0);
 if(ret < 0)
   {ret= 0; goto ex;}
 if(ret > 0)
   old_idx= idx;

 /* Handle eventual hardlink joining : */

 if(follow_links)
   ret= stat(disk_path, &stbuf);
 else
   ret= lstat(disk_path, &stbuf);
 if(ret==-1)
   {ret= 0; goto ex;}

 /* Are there new dev-ino-siblings in the image ? */
 /* Fake isofs.di */
 if(!(flag & 1)) {
   ret= Xorriso_record_dev_inode(xorriso, disk_path, stbuf.st_dev,
                                 stbuf.st_ino, node, iso_rr_path, 1);
   if(ret <= 0)
     {ret= -1; goto ex;}
   did_fake_di= 1;
   /* temporarily remove node from di_array so it does not disturb 
       search by its fake di info */;
   if(old_idx >= 0)
     xorriso->di_array[old_idx]= NULL;
 }
 ret= Xorriso_search_di_range(xorriso, node, &idx, &low, &high, 1);
 if(did_fake_di) {
   /* Revoke fake of isofs.di */
   hret= Xorriso_record_dev_inode(xorriso, disk_path, old_dev, old_ino,
                                  node, iso_rr_path, 1);
   if(hret <= 0)
     {ret= -1; goto ex;}
   if(old_idx >= 0)
     xorriso->di_array[old_idx]= node;
 }
 if(ret == 0)
   {ret= 1; goto ex;}
 if(ret < 0)
   {ret= 0; goto ex;}


#ifdef Xorriso_hardlink_update_debuG
 /* <<< */
 if(low < high || idx < 0) {
   fprintf(stderr,
   "xorriso_DEBUG: old_idx= %d , low= %d , high= %d , iso= '%s' , disk='%s'\n",
   old_idx, low, high, iso_rr_path, disk_path);
   fprintf(stderr,
         "xorriso_DEBUG: old_dev= %lu , old_ino= %lu ,  dev= %lu , ino= %lu\n",
         (unsigned long) old_dev, (unsigned long) old_ino,
         (unsigned long) stbuf.st_dev, (unsigned long) stbuf.st_ino);

   if(idx >= 0 && idx != old_idx)
     fprintf(stderr, "xorriso_DEBUG: idx= %d , old_idx = %d\n", idx, old_idx);
 }
#endif /* Xorriso_hardlink_update_debuG */

 /* Overwrite all valid siblings : */
 for(i= low; i <= high; i++) {
   if(i == idx || xorriso->di_array[i] == NULL)
 continue;

#ifdef Xorriso_hardlink_update_debuG
 /* <<< */
{
 ino_t ino;
 dev_t dev;

 Xorriso__get_di(xorriso->di_array[i], &dev, &ino, 0);
 fprintf(stderr, "xorriso_DEBUG: iso_sibling= '%s' , dev= %lu , ino= %lu\n",
         node_path, (unsigned long) dev, (unsigned long) ino);
}
#endif /* Xorriso_hardlink_update_debuG */

   xorriso->di_do_widen[i / 8]|= 1 << (i % 8);
 }

 ret= 1;
ex:;
 if(do_overwrite)
   *compare_result|= (1<<15);
 if(old_idx >= 0 && (*compare_result & (3 << 21))) {
   /* The old di info is obsolete */
   if(xorriso->di_array[old_idx] != NULL)
     iso_node_unref(xorriso->di_array[old_idx]);
   xorriso->di_array[old_idx]= NULL;
 }
 return(ret);
}


/* @param flag bit0= do not destroy di_array
*/
int Xorriso_finish_hl_update(struct XorrisO *xorriso, int flag)
{
 int ret, zero= 0;
 char *argv[4];
 struct Xorriso_lsT *disk_lst, *iso_lst;

 if(xorriso->di_array == NULL)
   {ret= 1; goto ex;}
 disk_lst= xorriso->di_disk_paths;
 iso_lst= xorriso->di_iso_paths;
 while(disk_lst != NULL && iso_lst != NULL) {
   argv[0]= Xorriso_lst_get_text(iso_lst, 0);
   argv[1]= "-exec";
   argv[2]= "widen_hardlinks";
   argv[3]= Xorriso_lst_get_text(disk_lst, 0);
   zero= 0;
   ret= Xorriso_option_find(xorriso, 4, argv, &zero, 0); /* -findi */
   if(ret < 0)
     goto ex;
   disk_lst= Xorriso_lst_get_next(disk_lst, 0);
   iso_lst= Xorriso_lst_get_next(iso_lst, 0);
 }
 ret= 1;
ex:;
 if(!(flag & 1))
   Xorriso_destroy_di_array(xorriso, 0);
 return(ret);
}


/*
   @param flag      bit0= do not report to result but only retrieve md5 text
   @return          1= ok, 0= no md5 available, <0= other error
*/
int Xorriso_get_md5(struct XorrisO *xorriso, void *in_node, char *path,
                    char md5[16], int flag)
{
 int ret= 1, i;
 char *wpt;
 IsoImage *image;
 IsoNode *node;

 ret= Xorriso_get_volume(xorriso, &image, 0);
 if(ret <= 0)
   goto ex;
 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret<=0)
     goto ex;
 }
 if(!LIBISO_ISREG(node))
   return(0);
 ret= iso_file_get_md5(image, (IsoFile *) node, md5, 0);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret <= 0)
   goto ex;
 if(flag & 1)
   {ret= 1; goto ex;}

 wpt= xorriso->result_line;
 for(i= 0; i < 16; i++) {
   sprintf(wpt, "%2.2x", ((unsigned char *) md5)[i]);
   wpt+= 2;
 }
 strcpy(wpt, "  ");
 wpt+= 2;
 Xorriso_getfname(xorriso, path, 1 | 2);
 ret= 1;
ex:;
 return(ret);
}


int Xorriso_make_md5(struct XorrisO *xorriso, void *in_node, char *path,
                     int flag)
{
 int ret;
 off_t size;
 IsoNode *node;
 
 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret <= 0)
     return(ret);
 }
 if(!LIBISO_ISREG(node))
   return(0);
 ret= iso_file_make_md5((IsoFile *) node, 0);
 size= iso_file_get_size((IsoFile *) node);
 xorriso->pacifier_count+= size;
 xorriso->pacifier_byte_count+= size;
 Xorriso_pacifier_callback(xorriso, "content bytes read",
                           xorriso->pacifier_count, 0, "", 0);
 Xorriso_process_msg_queues(xorriso, 0);
 if(ret < 0) {
   Xorriso_report_iso_error(xorriso, "", ret,
                            "Error when computing MD5", 0, "FAILURE", 1);
   return(0);
 }
 Xorriso_set_change_pending(xorriso, 1);
 return(1);
}


/* @param node      Opaque handle to IsoNode which is to be inquired instead of                     path if it is not NULL.
   @param path      is used as address if node is NULL.
   @param flag      bit0= do not report to result but only indicate outcome
                          by return value
                    bit1= silently ignore nodes without MD5
                    bit2= do not only report mismatches but also matches
   @return          3= not a data file
                    2= no MD5 attached to node
                    1= ok, MD5 compared and matching
                    0= not ok, MD5 mismatch
                   <0= other error
*/
int Xorriso_check_md5(struct XorrisO *xorriso, void *in_node, char *path,
                      int flag)
{
 int ret, wanted, rret;
 IsoImage *image;
 IsoNode *node;
 IsoFile *file;
 char node_md5[16], data_md5[16], buffer[64 * 1024];
 void *stream= NULL, *ctx= NULL;
 off_t todo;

 node= (IsoNode *) in_node;
 if(node == NULL) {
   ret= Xorriso_get_node_by_path(xorriso, path, NULL, &node, 0);
   if(ret<=0)
     {ret= -1; goto ex;}
 }
 if(!LIBISO_ISREG(node)) {
   strcpy(xorriso->info_text, "-check_md5: Not a data file: ");
   Text_shellsafe(path, xorriso->info_text, 1);
   if(!(flag & 2))
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   ret= 3; goto ex;
 }
 file= (IsoFile *) node;

 /* obtain MD5 */
 ret= Xorriso_get_volume(xorriso, &image, 0);
 if(ret <= 0)
   {ret= -1; goto ex;}
 ret= iso_file_get_md5(image, file, node_md5, 0);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret < 0)
   {ret= -1; goto ex;}
 if(ret == 0) {
   strcpy(xorriso->info_text, "-check_md5: No MD5 recorded with file: ");
   Text_shellsafe(path, xorriso->info_text, 1);
   if(!(flag & 2))
     Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "NOTE", 0);
   ret= 2; goto ex;
 }

 /* Read file and compute MD5 */;
 ret= Xorriso_iso_file_open(xorriso, path, (void *) node, &stream, 1 | 2);
 if(ret <= 0)
   {ret= -1; goto ex;}
 ret= iso_md5_start(&ctx);
 if(ret < 0)
     goto ex;
 todo= iso_stream_get_size(stream);
 while(todo > 0) {
   if(todo < sizeof(buffer))
     wanted= todo;
   else
     wanted= sizeof(buffer);
   rret = Xorriso_iso_file_read(xorriso, stream, buffer, wanted, 0);
   if(rret <= 0)
     {ret= -1; goto ex;}
   todo-= rret;
   ret = iso_md5_compute(ctx, buffer, rret);
   if(ret < 0)
     goto ex;
   xorriso->pacifier_count+= rret; 
   xorriso->pacifier_byte_count+= rret;
   Xorriso_pacifier_callback(xorriso, "content bytes read",
                             xorriso->pacifier_count, 0, "", 0);
 }
 ret= iso_md5_end(&ctx, data_md5);
 if(ret < 0)
     goto ex;

 /* Report outcome */
 Xorriso_process_msg_queues(xorriso,0);
 if(! iso_md5_match(node_md5, data_md5)) {
   sprintf(xorriso->result_line, "MD5 MISMATCH: ");
   Text_shellsafe(path, xorriso->result_line, 1);
   strcat(xorriso->result_line, "\n");
   if(!(flag & 1))
     Xorriso_result(xorriso,0);
   ret= 0;
 } else {
   sprintf(xorriso->result_line, "md5 match   : ");
   Text_shellsafe(path, xorriso->result_line, 1);
   strcat(xorriso->result_line, "\n");
   if(flag & 4)
     Xorriso_result(xorriso,0);
   ret= 1;
 }

ex:;
 Xorriso_process_msg_queues(xorriso,0);
 Xorriso_iso_file_close(xorriso, &stream, 0);
 if(ctx != NULL)
   iso_md5_end(&ctx, data_md5);
 if(ret < 0) {
   sprintf(xorriso->result_line, "NOT READABLE: ");
   Text_shellsafe(path, xorriso->result_line, 1);
   strcat(xorriso->result_line, "\n");
   if(!(flag & 1))
     Xorriso_result(xorriso,0);
 }
 return(ret);
}


int Xorriso_check_md5_range(struct XorrisO *xorriso, off_t start_lba,
                            off_t end_lba, char md5[16], int flag)
{
 int ret;
 struct burn_drive_info *dinfo= NULL;
 struct burn_drive *drive= NULL;
 off_t pos, data_count, to_read;
 char data[64 * 1024], data_md5[16];
 void *ctx = NULL;

 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                                "on attempt to check session MD5 checksum", 0);
 if(ret <= 0)
   goto ex;
 ret= iso_md5_start(&ctx);
 if(ret <= 0) {
   Xorriso_no_malloc_memory(xorriso, NULL, 0);
   goto ex;
 }
 for(pos= start_lba; pos < end_lba; pos+= 32) {
   to_read= 32;
   if(pos + to_read > end_lba)
     to_read= end_lba - pos; 
   ret= burn_read_data(drive, pos * (off_t) 2048, data,
                       to_read * (off_t) 2048, &data_count, 0);
   if(ret <= 0)
     goto ex;
   iso_md5_compute(ctx, data, (int) data_count);
   xorriso->pacifier_count+= data_count; 
   xorriso->pacifier_byte_count+= data_count;
   Xorriso_pacifier_callback(xorriso, "content bytes read",
                             xorriso->pacifier_count, 0, "", 0);
 }
 iso_md5_end(&ctx, data_md5);
 ret= 1;
 if(! iso_md5_match(md5, data_md5))
   ret= 0;
ex:;
 Xorriso_process_msg_queues(xorriso,0);
 if(ctx != NULL)
   iso_md5_end(&ctx, data_md5);
 return(ret);
}


int Xorriso_check_session_md5(struct XorrisO *xorriso, char *severity,
                              int flag)
{
 int ret, i;
 IsoImage *image;
 uint32_t start_lba, end_lba;
 char md5[16], md5_text[33];

 ret= Xorriso_get_volume(xorriso, &image, 0);
 if(ret<=0)
   return(ret);
 ret= iso_image_get_session_md5(image, &start_lba, &end_lba, md5, 0);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret < 0)
   return(ret);
 if(ret == 0) {
   sprintf(xorriso->info_text,
           "No session MD5 is recorded with the loaded session");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "WARNING", 0);
   return(0);
 }

 sprintf(xorriso->info_text, "Checking loaded session by its recorded MD5.\n"); 
 Xorriso_info(xorriso, 0);
 for(i= 0; i < 16; i++)
   sprintf(md5_text + 2 * i, "%2.2x", ((unsigned char *) md5)[i]);
 sprintf(xorriso->result_line,
         "Session MD5 %s , LBA %.f , %.f blocks\n",
         md5_text, (double) start_lba, (double) end_lba - start_lba);
 Xorriso_result(xorriso,0);
 ret= Xorriso_check_md5_range(xorriso, (off_t) start_lba, (off_t) end_lba,
                              md5, 0);
 return(ret);
}
 

int Xorriso_image_has_md5(struct XorrisO *xorriso, int flag)
{
 int ret;
 IsoImage *image;
 uint32_t start_lba, end_lba;
 char md5[16];

 ret= Xorriso_get_volume(xorriso, &image, 0);
 if(ret<=0)
   return(ret);
 ret= iso_image_get_session_md5(image, &start_lba, &end_lba, md5, 0);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret <= 0)
   return(0);
 return(1);
}


int Xorriso_md5_start(struct XorrisO *xorriso, void **ctx, int flag)
{
 int ret;

 ret= iso_md5_start(ctx);
 if(ret == 1)
   return(1);
 Xorriso_no_malloc_memory(xorriso, NULL, 0);
 return(-1);
}


int Xorriso_md5_compute(struct XorrisO *xorriso, void *ctx,
                        char *data, int datalen, int flag)
{
 iso_md5_compute(ctx, data, datalen);
 return(1);
}


int Xorriso_md5_end(struct XorrisO *xorriso, void **ctx, char md5[16],
                    int flag)
{
 int ret;

 ret= iso_md5_end(ctx, md5);
 Xorriso_process_msg_queues(xorriso,0);
 if(ret <= 0)
   return(0);
 return(1);
}


/* @param flag bit0=input drive
               bit1=output drive
               bit2= wake up rather than calm down
*/
int Xorriso_drive_snooze(struct XorrisO *xorriso, int flag)
{
 int in_is_out_too, ret;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 
 in_is_out_too= (xorriso->in_drive_handle == xorriso->out_drive_handle);
 if((flag & 1) && xorriso->in_drive_handle != NULL) {
   Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                             "on attempt to calm drive", 0);
   burn_drive_snooze(drive, !!(flag & 4));
   if(in_is_out_too)
     {ret= 1; goto ex;}
 }
 if((flag&2) && xorriso->out_drive_handle!=NULL) {
   Xorriso_get_drive_handles(xorriso, &dinfo, &drive,
                             "on attempt to calm drive", 2);
   burn_drive_snooze(drive, !!(flag & 4));
 }
 ret= 1;
ex:;
 Xorriso_process_msg_queues(xorriso,0);
 return(ret);
}


int Xorriso_pvd_info(struct XorrisO *xorriso, int flag)
{
 int ret, msc1= -1, msc2, i;
 IsoImage *image;
 struct burn_drive_info *dinfo;
 struct burn_drive *drive;
 char *msg, block_head[8];
 off_t head_count;

 msg= xorriso->result_line;
 ret= Xorriso_get_volume(xorriso, &image, 0);
 if(ret<=0)
   return(ret);
 ret= Xorriso_get_drive_handles(xorriso, &dinfo, &drive, "", 16);
 if(ret > 0) {
   ret= Xorriso_msinfo(xorriso, &msc1, &msc2, 1 | 4);
   if(ret<0)
     return(ret);
   Xorriso_toc(xorriso, 128);
   if(msc1 >= 0) {
     for(i = msc1 + 16; i < msc1 + 32; i++) {
       ret= burn_read_data(drive, (off_t) i * (off_t) 2048, block_head,
                           (off_t) sizeof(block_head), &head_count, 2);
       if(ret <= 0) {
         i= msc1 + 32;
     break;
       }
       if(block_head[0] == 1 && strncmp(block_head + 1, "CD001", 5) == 0)
     break;
     }
     if(i < msc1 + 32) {
       sprintf(msg, "PVD address  : %ds\n", i);
       Xorriso_result(xorriso,0);
     }
   }
 }
 sprintf(msg, "Volume Id    : %s\n", un0(iso_image_get_volume_id(image)));
 Xorriso_result(xorriso,0);
 sprintf(msg, "Volume Set Id: %s\n", xorriso->volset_id);
 Xorriso_result(xorriso,0);
 sprintf(msg, "Publisher Id : %s\n", xorriso->publisher);
 Xorriso_result(xorriso,0);
 sprintf(msg, "Preparer Id  : %s\n",
         un0(iso_image_get_data_preparer_id(image)));
 Xorriso_result(xorriso,0);
 sprintf(msg, "App Id       : %s\n", xorriso->application_id);
 Xorriso_result(xorriso,0);
 sprintf(msg, "System Id    : %s\n", xorriso->system_id);
 Xorriso_result(xorriso,0);
 sprintf(msg, "Copyright Id : %s\n",
         un0(iso_image_get_copyright_file_id(image)));
 Xorriso_result(xorriso,0);
 sprintf(msg, "Abstract Id  : %s\n",
         un0(iso_image_get_abstract_file_id(image)));
 Xorriso_result(xorriso,0);
 sprintf(msg, "Biblio Id    : %s\n", un0(iso_image_get_biblio_file_id(image)));
 Xorriso_result(xorriso,0);
 return(1);
}


/* @param flag bit0= do not set hln_change_pending */
int Xorriso_set_change_pending(struct XorrisO *xorriso, int flag)
{
 int ret;
 IsoImage *image;

 ret= Xorriso_get_volume(xorriso, &image, 1);
 if(ret <= 0)
   return ret;
 xorriso->volset_change_pending= 1;
 if(!(flag & 1))
   xorriso->hln_change_pending= 1;
 return(1);
}

