
/* xorriso - creates, loads, manipulates and burns ISO 9660 filesystem images.

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

   Provided under GPL version 2 or later.

   This file contains the implementations of classes:

   - SplitparT which represents byte intervals of data files.

   - DirseQ which crawls along a directory's content list.

   - ExclusionS which manages the list of excluded file paths and
     leaf patterns.

   - Xorriso_lsT which provides a generic double-linked list.

   - LinkiteM, PermiteM which temporarily record relations and states.

*/

#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif

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


#include "xorriso.h"
#include "xorriso_private.h"


/* ---------------------------- SplitparT ------------------------- */


struct SplitparT {
 char *name;
 int partno;
 int total_parts;
 off_t offset;
 off_t bytes;
 off_t total_bytes;
};

static char Splitpart_wordS[][16]= {"part_", "_of_", "_at_", "_with_", "_of_"};


int Splitparts_new(struct SplitparT **o, int count, int flag)
{
 int i;

 (*o)= TSOB_FELD(struct SplitparT, count);
 if((*o)==NULL)
   return(-1);
 for(i= 0; i<count; i++) {
   (*o)[i].name= NULL;
   (*o)[i].partno= 0;
   (*o)[i].total_parts= 0;
   (*o)[i].offset= 0;
   (*o)[i].bytes= 0;
   (*o)[i].total_bytes= 0;
 }
 return(1);
}


int Splitparts_destroy(struct SplitparT **o, int count, int flag)
{
 int i;

 if((*o)==NULL)
   return(0);
 for(i= 0; i<count; i++) {
   if((*o)[i].name!=NULL)
     free((*o)[i].name);
 }
 free(*o);
 *o= NULL;
 return(1);
}


int Splitparts_set(struct SplitparT *o, int idx,
                   char *name, int partno, int total_parts,
                   off_t offset, off_t bytes, off_t total_bytes, int flag)
{
 if(o[idx].name!=NULL)
   free(o[idx].name);
 o[idx].name= strdup(name);
 if(o[idx].name==NULL)
   return(-1);
 o[idx].partno= partno;
 o[idx].total_parts= total_parts;
 o[idx].offset= offset;
 o[idx].bytes= bytes;
 o[idx].total_bytes= total_bytes;
 return(1);
}


int Splitparts_get(struct SplitparT *o, int idx, char **name, int *partno,
                   int *total_parts, off_t *offset, off_t *bytes,
                   off_t *total_bytes, int flag)
{
 *name= o[idx].name;
 *partno= o[idx].partno;
 *total_parts= o[idx].total_parts;
 *offset= o[idx].offset;
 *bytes= o[idx].bytes;
 *total_bytes= o[idx].total_bytes;
 return(1);
}


int Splitpart__read_next_num(char *base_pt, char **next_pt, off_t *num,
                             int flag)
{
 char *cpt, *ept, scale[4];
 double sfak;

 *num= 0;
 for(cpt= base_pt; *cpt!=0 && !isdigit(*cpt); cpt++);
 if(*cpt==0)
   return(0);
 for(ept= cpt; *ept!=0 && isdigit(*ept); ept++)
   *num= (*num)*10+(*ept)-'0';
 scale[0]= '1';
 scale[1]= *ept;
 scale[2]= 0;
 sfak= Scanf_io_size(scale, 0);
 *num *= (off_t) sfak;
 if(sfak > 1.0)
   ept++;
 *next_pt= ept;
 return(1);
}


int Splitpart__parse(char *name, int *partno, int *total_parts,
                    off_t *offset, off_t *bytes, off_t *total_bytes, int flag)

{
 int ret; 
 off_t num;
 char *cpt, *ept;

 cpt= name;
 if(strncmp(cpt, Splitpart_wordS[0], strlen(Splitpart_wordS[0])) != 0)
   return(0);
 ret= Splitpart__read_next_num(cpt, &ept, &num, 0);
 if(ret<=0)
   return(ret);
 *partno= num;
 cpt= ept;
 if(strncmp(cpt, Splitpart_wordS[1], strlen(Splitpart_wordS[1])) != 0)
   return(0);
 ret= Splitpart__read_next_num(cpt, &ept, &num, 0);
 if(ret<=0)
   return(ret);
 *total_parts= num;
 cpt= ept;
 if(strncmp(cpt, Splitpart_wordS[2], strlen(Splitpart_wordS[2])) != 0)
   return(0);
 ret= Splitpart__read_next_num(cpt, &ept, offset, 0);
 if(ret<=0)
   return(ret);
 cpt= ept;
 if(strncmp(cpt, Splitpart_wordS[3], strlen(Splitpart_wordS[3])) != 0)
   return(0);
 ret= Splitpart__read_next_num(cpt, &ept, bytes, 0);
 if(ret<=0)
   return(ret);
 cpt= ept;
 if(strncmp(cpt, Splitpart_wordS[4], strlen(Splitpart_wordS[4])) != 0)
   return(0);
 ret= Splitpart__read_next_num(cpt, &ept, total_bytes, 0);
 if(ret<=0)
   return(ret);
 if(*ept != 0)
   return(0);
 return(1);
}


int Splitpart__is_part_path(char *path, int flag)
{
 int partno, total_parts, ret;
 off_t offset, bytes, total_bytes;
 char *name;

 name= strrchr(path, '/');
 if(name == NULL)
   name= path;
 else
   name++;
 ret= Splitpart__parse(name, &partno, &total_parts, &offset, &bytes,
                       &total_bytes, 0);
 return(ret > 0);
}


/* part_#_of_#_at_#_with_#_of_#
*/
int Splitpart__compose(char *adr, int partno, int total_parts,
                       off_t offset, off_t bytes, off_t total_bytes, int flag)
{
 sprintf(adr, "%s%d%s%d%s", Splitpart_wordS[0], partno, Splitpart_wordS[1],
                            total_parts, Splitpart_wordS[2]);
 if((offset % (1024*1024))==0 && offset>0) {
   Sfile_off_t_text(adr+strlen(adr), offset / (1024*1024), 0);
   strcat(adr, "m");
 } else
   Sfile_off_t_text(adr+strlen(adr), offset, 0);
 strcat(adr, Splitpart_wordS[3]); 
 if((bytes % (1024*1024))==0) {
   Sfile_off_t_text(adr+strlen(adr), bytes / (1024*1024), 0);
   strcat(adr, "m");
 } else
   Sfile_off_t_text(adr+strlen(adr), bytes, 0);
 strcat(adr, Splitpart_wordS[4]);
 Sfile_off_t_text(adr+strlen(adr), total_bytes, 0);
 return(1);
}


int Splitparts_cmp(const void *v1, const void *v2)
{
 struct SplitparT *p1, *p2;

 p1= (struct SplitparT *) v1;
 p2= (struct SplitparT *) v2;
 
 if(p1->partno>p2->partno)
   return(1);
 if(p1->partno<p2->partno)
   return(-1);
 if(p1->offset>p2->offset)
   return(1);
 if(p1->offset<p2->offset)
   return(-1);
 return(0);
}


int Splitparts_sort(struct SplitparT *o, int count, int flag)
{
 qsort(o, (size_t) count, sizeof(struct SplitparT), Splitparts_cmp);
 return(1);
}


/* ---------------------------- End SplitparT ------------------------- */


/* ------------------------------ DirseQ  ------------------------------ */


static int Dirseq_buffer_sizE= 100;

struct DirseQ {
  char adr[SfileadrL];
  DIR *dirpt;
  int count;  
  char **buffer;
  int buffer_size;
  int buffer_fill;
  int buffer_rpt;

  struct DirseQ *next;
};

int Dirseq_destroy(struct DirseQ **o, int flag);
int Dirseq_next_adrblock(struct DirseQ *o, char *replies[], int *reply_count,
                         int max_replies, int flag);


int Dirseq_new(struct DirseQ **o, char *adr, int flag)
/*
 bit0= with non-fatal errors do not complain about failed opendir() 
*/
{
 int ret,i,severe_error;
 struct DirseQ *m;

 m= *o= TSOB_FELD(struct DirseQ,1);
 if(m==NULL)
   return(-1);
 m->adr[0]= 0;
 m->dirpt= NULL;
 m->count= 0;
 m->buffer= NULL;
 m->buffer_size= 0;
 m->buffer_fill= 0;
 m->buffer_rpt= 0;
 m->next= NULL;
 if(Sfile_str(m->adr, adr, 0)<=0)
   {ret= 0; goto failed;}
 m->buffer= TSOB_FELD(char *,Dirseq_buffer_sizE);
 if(m->buffer==NULL)
   {ret= -1; goto failed;}
 m->buffer_size= Dirseq_buffer_sizE;
 for(i= 0;i<m->buffer_size;i++)
   m->buffer[i]= NULL;
 if(adr[0]==0)
   m->dirpt= opendir(".");
 else
   m->dirpt= opendir(adr);
 if(m->dirpt==NULL) {
   severe_error= (errno && errno!=ENOENT && errno!=EACCES && errno!=ENOTDIR);
   if(severe_error || !(flag&1))
     fprintf(stderr,"opendir(%s) failed : %s\n",adr,strerror(errno));
   ret= -severe_error;
   goto failed;
 }
 return(1);
failed:;
 Dirseq_destroy(o,0);
 return(ret);
}


int Dirseq_destroy(struct DirseQ **o, int flag)
{
 int i;

 if(*o==NULL)
   return(0);
 if((*o)->dirpt!=NULL)
   closedir((*o)->dirpt);
 if((*o)->buffer!=NULL) {
   for(i=0;i<(*o)->buffer_size;i++)
     if((*o)->buffer[i]!=NULL)
       free((*o)->buffer[i]);
   free((char *) (*o)->buffer);
 }
 free((char *) *o);
 (*o)= NULL;
 return(1);
}


int Dirseq_set_next(struct DirseQ *o, struct DirseQ *next, int flag)
{
 o->next= next;
 return(1);
}


int Dirseq_get_next(struct DirseQ *o, struct DirseQ **next, int flag)
{
 *next= o->next;
 return(1);
}


int Dirseq_get_adr(struct DirseQ *o, char **adrpt, int flag)
{
 *adrpt= o->adr;
 return(1);
}


int Dirseq_rewind(struct DirseQ *o, int flag)
{
 rewinddir(o->dirpt);
 return(1);
}


int Dirseq_next_adr(struct DirseQ *o, char reply[SfileadrL], int flag)
/*
flag:
 bit0= permission to use buffer
 bit1= do not increment counter
 bit2= ignore buffer in any case
 bit3= do not exclude '.' and '..'
 bit4= sort buffer
 bit5= sort only incomplete last buffer
return:
 <0 error
 0= no more entries available
 1= ok, reply is valid
*/
{
 int ret;
 struct dirent *entry;
 char *name;

 static int override_flag_0= 0,override_flag_1= 32;
 flag= (flag&~override_flag_0)|override_flag_1;

 if((flag&1) && o->buffer_rpt>=o->buffer_fill) {
   /* permission to buffer and buffer empty : load a buffer */
   ret= Dirseq_next_adrblock(o,o->buffer,&(o->buffer_fill),
                             o->buffer_size,2|4|(flag&16));
   if(ret<=0)
     return(ret);
   o->buffer_rpt= 0;
   if((flag&32) && o->buffer_fill<o->buffer_size && o->buffer_fill>0) 
     Sort_argv(o->buffer_fill,o->buffer,0);
 }
 if(o->buffer_rpt<o->buffer_fill && !(flag&4)) {
   ret= Sfile_str(reply,o->buffer[o->buffer_rpt],0);
   Sregex_string(&(o->buffer[o->buffer_rpt]),NULL,0);
   if(ret<=0)
     return(-1);
   (o->buffer_rpt)++;
   if(!(flag&2))
     o->count++;
   return(1);
 }
 do {
   entry= readdir(o->dirpt);
   if(entry==NULL) {
     /* >>> how to distinguish error from EOF , do i need a (FILE *) ? */
     return(0);
   }
   if(strlen(entry->d_name)>=SfileadrL) {
     fprintf(stderr,"--- oversized directory entry (number %d) :\n    %s",
                    o->count+1,entry->d_name);
     return(-1);
   }
   name= entry->d_name;
   if(flag&8)
 break;
   /* skip "." and ".." */
 } while(name[0]=='.' && ((name[1]=='.' && name[2]==0) || name[1]==0));
 if(Sfile_str(reply,name,0)<=0)
   return(-1);
 if(!(flag&2))
   o->count++;
 return(1);
}


int Dirseq_next_adrblock(struct DirseQ *o, char *replies[], int *reply_count,
                         int max_replies, int flag)
/* @param replies A vector of Sregex_string pointers */
/*
flag:
 bit0= permission to use buffer
 bit1= do not increment counter
 bit2= ignore buffer in any case
 bit4= sort replies
return:
 <0 error
 0= no more entries available
 1= ok, reply is valid
*/
{
 int i,ret;
 char reply[SfileadrL];

 *reply_count= 0;
 for(i=0;i<max_replies;i++) {
   ret= Dirseq_next_adr(o,reply,flag&(1|2|4));
   if(ret<0)
     return(ret);
   if(ret==0)
 break;
   if(Sregex_string(&(replies[i]),reply,0)<=0)
     return(-1);
   (*reply_count)++;
 }
 if((*reply_count)==0)
   return(0);
 if(flag&16) 
   Sort_argv(*reply_count,replies,0);
 return(1);
}


/* ---------------------------- End DirseQ  ----------------------------- */


/* ---------------------------- Xorriso_lsT ----------------------------- */


/*
 @param flag Bitfield for control purposes
        bit0= insert before link rather than after it
        bit1= do not copy data (e.g. because *data is invalid)
        bit2= attach data directly by pointer rather than by copying
*/
int Xorriso_lst_new_binary(struct Xorriso_lsT **lstring, char *data,
                           int data_len, struct Xorriso_lsT *link, int flag)
{
 int ret;
 struct Xorriso_lsT *s;

 s= TSOB_FELD(struct Xorriso_lsT,1);
 if(s==NULL)
   return(-1);
 s->text= NULL;
 s->next= s->prev= NULL;

 if(flag & 4) {
   s->text= data;
 } else {
   if(data_len<=0)
     {ret= -1; goto failed;}
   s->text= Smem_malloC(data_len);
   if(s->text==NULL)
     {ret= -1; goto failed;}
   if(!(flag&2))
     memcpy(s->text,data,data_len);
 }

 if(link==NULL) {
   ;
 } else if(flag&1) {
   s->next= link;
   s->prev= link->prev;
   if(link->prev!=NULL) 
     link->prev->next= s;
   link->prev= s;
 } else {
   s->prev= link;
   s->next= link->next;
   if(link->next!=NULL) 
     link->next->prev= s;
   link->next= s;
 }
 *lstring= s;
 return(1);
failed:;
 *lstring= s;
 Xorriso_lst_destroy(lstring,0);
 return(-1);
}


/*
 @param flag Bitfield for control purposes
             see Xorriso_lst_new_binary()
*/
int Xorriso_lst_new(struct Xorriso_lsT **lstring, char *text,
                    struct Xorriso_lsT *link, int flag)
{
 int ret;

 ret= Xorriso_lst_new_binary(lstring,text,strlen(text)+1,link,flag);
 return(ret);
}


/*
 @param flag Bitfield for control purposes
             bit0= do not set *lstring to NULL
*/
int Xorriso_lst_destroy(struct Xorriso_lsT **lstring, int flag)
{
 struct Xorriso_lsT *s;

 s= *lstring;
 if(s==NULL)
   return(0);
 if(s->prev!=NULL)
   s->prev->next= s->next;
 if(s->next!=NULL)
   s->next->prev= s->prev;
 if(s->text!=NULL)
   Smem_freE(s->text);
 Smem_freE((char *) s);
 if(!(flag&1))
   *lstring= NULL;
 return(1);
}


int Xorriso_lst_destroy_all(struct Xorriso_lsT **lstring, int flag)
{
 struct Xorriso_lsT *s,*next;

 if(lstring==NULL)
   return(-1);
 if((*lstring)==NULL)
   return(0);
 for(s= *lstring; s->prev!=NULL; s= s->prev);
 for(;s!=NULL;s= next){
   next= s->next;
   Xorriso_lst_destroy(&s,0);
 }
 *lstring= NULL;
 return(1);
}


int Xorriso_lst_append_binary(struct Xorriso_lsT **entry,
                              char *data, int data_len, int flag)
{
 struct Xorriso_lsT *target= NULL,*newby;

 if(*entry!=NULL)
   for(target= *entry; target->next!=NULL; target= target->next);
 if(Xorriso_lst_new_binary(&newby, data, data_len, target, flag & ~1)<=0)
   return(-1);
 if(*entry==NULL || (flag & 1))
   *entry= newby;
 return(1);
}


struct Xorriso_lsT *Xorriso_lst_get_next(struct Xorriso_lsT *entry, int flag)
{
 return(entry->next);
}


struct Xorriso_lsT *Xorriso_lst_get_prev(struct Xorriso_lsT *entry, int flag)
{
 return(entry->prev);
}


char *Xorriso_lst_get_text(struct Xorriso_lsT *entry, int flag)
{
 return(entry->text);
}


int Xorriso_lst_detach_text(struct Xorriso_lsT *entry, int flag)
{
 entry->text= NULL;
 return(1);
}


/* --------------------------- End Xorriso_lsT ---------------------------- */


/* ------------------------------ ExclusionS ------------------------------ */


struct ExclusionS {

 /* Absolute input patterns which lead to not_paths */
 struct Xorriso_lsT *not_paths_descr;

 /* Actually banned absolute paths */
 struct Xorriso_lsT *not_paths;

 /* Input patterns which lead to not_leafs */
 struct Xorriso_lsT *not_leafs_descr;

 /* Compiled not_leaf patterns. Caution: not char[] but  regex_t */
 struct Xorriso_lsT *not_leafs;
 
};


int Exclusions_new(struct ExclusionS **o, int flag)
{
 struct ExclusionS *m;

 m= *o= TSOB_FELD(struct ExclusionS, 1);
 if(m==NULL)
   return(-1);
 m->not_paths_descr= NULL;
 m->not_paths= NULL;
 m->not_leafs_descr= NULL;
 m->not_leafs= NULL;
 return(1);
}


int Exclusions_destroy(struct ExclusionS **o, int flag)
{
 struct Xorriso_lsT *s,*next;

 if((*o)==NULL)
   return(0);
 Xorriso_lst_destroy_all(&((*o)->not_paths_descr), 0);
 Xorriso_lst_destroy_all(&((*o)->not_paths), 0);
 Xorriso_lst_destroy_all(&((*o)->not_leafs_descr), 0);
 for(s= (*o)->not_leafs; s!=NULL; s= next){
   next= s->next;
   regfree((regex_t *) s->text);
   Xorriso_lst_destroy(&s, 0);
 }
 free((char *) *o);
 (*o)= NULL;
 return(1);
}


int Exclusions_add_not_paths(struct ExclusionS *o, int descrc, char **descrs,
                             int pathc, char **paths, int flag)
{
 struct Xorriso_lsT *s, *new_s;
 int i, ret;

 s= NULL;
 if(o->not_paths_descr!=NULL) 
   for(s= o->not_paths_descr; s->next!=NULL; s= s->next);
 for(i= 0; i<descrc; i++) {
   ret= Xorriso_lst_new(&new_s, descrs[i], s, 0);
   if(ret<=0)
     return(ret);
   if(o->not_paths_descr==NULL)
     o->not_paths_descr= new_s;
   s= new_s;
 }
 s= NULL;
 if(o->not_paths!=NULL) 
   for(s= o->not_paths; s->next!=NULL; s= s->next);
 for(i= 0; i<pathc; i++) {
   ret= Xorriso_lst_new(&new_s, paths[i], s, 0);
   if(ret<=0)
     return(ret);
   if(o->not_paths==NULL)
     o->not_paths= new_s;
   s= new_s;
 }
 return(1);
}


/* @return -1=cannot store , 0=cannot compile regex , 1=ok
*/
int Exclusions_add_not_leafs(struct ExclusionS *o, char *not_leafs_descr,
                             regex_t *re, int flag)
{
 int ret;

 ret= Xorriso_lst_append_binary(&(o->not_leafs_descr),
                            not_leafs_descr, strlen(not_leafs_descr)+1, 0);
 if(ret<=0)
   return(-1);
 ret= Xorriso_lst_append_binary(&(o->not_leafs), (char *) re, sizeof(regex_t), 0);
 if(ret<=0)
   return(-1);
 return(1);
}


/* @param flag bit0= whole subtree is banned with -not_paths 
   @return 0=no match , 1=not_paths , 2=not_leafs, <0=error
*/
int Exclusions_match(struct ExclusionS *o, char *abs_path, int flag)
{
 struct Xorriso_lsT *s;
 char leaf[SfileadrL], *leaf_pt;
 regmatch_t match[1];
 int ret, was_non_slash, l;

 /* test abs_paths */
 if(flag&1) {
   for(s= o->not_paths; s!=NULL; s= s->next) {
     l= strlen(s->text);
     if(strncmp(abs_path, s->text, l)==0)
       if(abs_path[l]=='/' || abs_path[l]==0)
         return(1);
   }
 } else {
   for(s= o->not_paths; s!=NULL; s= s->next)
     if(strcmp(abs_path, s->text)==0)
       return(1);
 }

 /* determine leafname */
 was_non_slash= 0;
 for(leaf_pt= abs_path+strlen(abs_path); leaf_pt>abs_path; leaf_pt--) {
   if(*leaf_pt=='/') {
     if(was_non_slash) {
       leaf_pt++;
 break;
     }
   } else if(*leaf_pt!=0)
     was_non_slash= 1;
 }
 if(strlen(leaf_pt)>=SfileadrL)
   return(-1);
 strcpy(leaf, leaf_pt);
 leaf_pt= strchr(leaf, '/');
 if(leaf_pt!=NULL)
   *leaf_pt= 0;

 /* test with leaf expressions */
 for(s= o->not_leafs; s!=NULL; s= s->next) {
   ret= regexec((regex_t *) s->text, leaf, 1, match, 0);
   if(ret==0)
     return(2);
 }
 return(0);
}


int Exclusions_get_descrs(struct ExclusionS *o,
                          struct Xorriso_lsT **not_paths_descr,
                          struct Xorriso_lsT **not_leafs_descr, int flag)
{
 *not_paths_descr= o->not_paths_descr;
 *not_leafs_descr= o->not_leafs_descr;
 return(1);
}

/* ---------------------------- End ExclusionS ---------------------------- */


/* ------------------------------ LinkiteM -------------------------------- */

struct LinkiteM {
 char *link_path;
 dev_t target_dev;
 ino_t target_ino;
 int link_count;
 struct LinkiteM *next;
};


int Linkitem_new(struct LinkiteM **o, char *link_path, dev_t target_dev,
                 ino_t target_ino, struct LinkiteM *next, int flag)
{
 struct LinkiteM *m;

 m= *o= TSOB_FELD(struct LinkiteM,1);
 if(m==NULL)
   return(-1);
 m->target_dev= target_dev;
 m->target_ino= target_ino;
 m->next= next;
 m->link_count= 1;
 if(next!=NULL)
   m->link_count= m->next->link_count+1;
 m->link_path= strdup(link_path);
 if(m->link_path==NULL)
   goto failed;
 return(1);
failed:;
 Linkitem_destroy(o, 0);
 return(-1);
}


int Linkitem_destroy(struct LinkiteM **o, int flag)
{
 if((*o)==NULL)
   return(0);
 if((*o)->link_path!=NULL)
   free((*o)->link_path);
 free((char *) (*o));
 *o= NULL;
 return(1);
}


int Linkitem_reset_stack(struct LinkiteM **o, struct LinkiteM *to, int flag)
{
 struct LinkiteM *m, *m_next= NULL;

 /* Prevent memory corruption */
 for(m= *o;  m!=to; m= m->next)
   if(m==NULL) { /* this may actually not happen */
     *o= to;
     return(-1);
   }

 for(m= *o; m!=to; m= m_next) {
   m_next= m->next;
   Linkitem_destroy(&m, 0);
 }
 *o= to;
 return(1);
}


int Linkitem_find(struct LinkiteM *stack, dev_t target_dev, ino_t target_ino,
                  struct LinkiteM **result, int flag)
{
 struct LinkiteM *m;

 for(m= stack; m!=NULL; m= m->next) {
   if(target_dev == m->target_dev && target_ino == m->target_ino) {
     *result= m;
     return(1);
   }
 }
 return(0);
}


int Linkitem_get_link_count(struct LinkiteM *item, int flag)
{
 return(item->link_count);
}


/* ------------------------------ PermstacK ------------------------------- */


struct PermiteM {
 char *disk_path;
 struct stat stbuf;
 struct PermiteM *next;
};


int Permstack_push(struct PermiteM **o, char *disk_path, struct stat *stbuf,
                   int flag)
{
 struct PermiteM *m;

 m= TSOB_FELD(struct PermiteM,1);
 if(m==NULL)
   return(-1);
 m->disk_path= NULL;
 memcpy(&(m->stbuf), stbuf, sizeof(struct stat));
 m->next= *o;

 m->disk_path= strdup(disk_path);
 if(m->disk_path==NULL)
   goto failed;

 *o= m;
 return(1);
failed:;
 if(m->disk_path!=NULL)
   free(m->disk_path);
 free((char *) m);
 return(-1);
}


/* @param flag bit0= minimal transfer: access permissions only
               bit1= do not set timestamps
*/ 
int Permstack_pop(struct PermiteM **o, struct PermiteM *stopper,
                  struct XorrisO *xorriso, int flag)
{
 int ret;
 char sfe[5*SfileadrL];
 struct utimbuf utime_buffer;
 struct PermiteM *m, *m_next;

 if((*o)==stopper)
   return(1);
 for(m= *o; m!=NULL; m= m->next)
   if(m->next==stopper)
 break;
 if(m==NULL) {
   sprintf(xorriso->info_text,
           "Program error: Permstack_pop() : cannot find stopper");
   Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, 0, "FATAL", 0);
   return(-1);
 }

 for(m= *o; m!=stopper; m= m_next) {
   ret= chmod(m->disk_path, m->stbuf.st_mode);
   if(ret==-1) {
     if(xorriso!=NULL) {
       sprintf(xorriso->info_text,
             "Cannot change access permissions of disk directory: chmod %o %s",
             (unsigned int) (m->stbuf.st_mode & 07777),
             Text_shellsafe(m->disk_path, sfe, 0));
       Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",
                           0);
     }
   }
   if(!(flag&1)) {
     chown(m->disk_path, m->stbuf.st_uid, m->stbuf.st_gid);
                                               /* don't complain if it fails */
     if(!(flag&2)) {
       utime_buffer.actime= m->stbuf.st_atime;
       utime_buffer.modtime= m->stbuf.st_mtime;
       ret= utime(m->disk_path,&utime_buffer);
       if(ret==-1 && xorriso!=NULL) {
         sprintf(xorriso->info_text,
                 "Cannot change timestamps of disk directory: %s",
                 Text_shellsafe(m->disk_path, sfe, 0));
         Xorriso_msgs_submit(xorriso, 0, xorriso->info_text, errno, "FAILURE",
                             0);
       }
     }
   }
   m_next= m->next;
   free(m->disk_path);
   free((char *) m);
   *o= m_next;
 }
 return(1);
}


/* ---------------------------- End PermstacK ----------------------------- */

