
/*
  cc -g -c isoburn.c
*/

/*
  Class core of libisoburn.

  Copyright 2007 Vreixo Formoso Lopes <metalpain2002@yahoo.es>
             and Thomas Schmitt <scdbackup@gmx.net>
*/

/* ( derived from stub generated by CgeN on  Sat, 01 Sep 2007 12:04:36 GMT ) */

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <libburn/libburn.h>
#include <libisofs/libisofs.h>
#include "libisoburn.h"
#include "isoburn.h"



/* --------------------------  isoburn  ----------------------- */


/* The global list of isoburn objects. Usually there is only one.
   >>> we are not ready for multiple control threads yet. See >>> mutex .
   Multiple burns under one control thread should work.
*/
struct isoburn *isoburn_list_start= NULL;


int isoburn_new(struct isoburn **objpt, int flag)
{
 struct isoburn *o;
 int i;

 *objpt= o= (struct isoburn *) malloc(sizeof(struct isoburn));
 if(o==NULL)
   return(-1);

 o->drive= NULL;
 o->emulation_mode= 0;
 o->min_start_byte= 0;
 o->nwa= 0;
 o->fifo= NULL;
 o->wrote_well= -1;
 o->src= NULL;
 o->fabricated_disc_status= BURN_DISC_UNREADY;
 for(i=0;i<65536;i++)
   o->target_iso_head[i]= 0;
 o->target_volset= NULL;
 o->prev= NULL;
 o->next= NULL;

 isoburn_link(o, isoburn_list_start, 1);
 return(1);
}


int isoburn_destroy(struct isoburn **objpt, int flag)
{
 struct isoburn *o;

 o= *objpt;
 if(o==NULL)
   return(0);

 /* >>> mutex */

 if(o==isoburn_list_start)
   isoburn_list_start= o->next;
 if(o->prev!=NULL)
   o->prev->next= o->next;
 if(o->next!=NULL)
   o->next->prev= o->prev;

 /* >>> end mutex */

 if(o->target_volset!=NULL)
   iso_volset_free(o->target_volset);
 if(o->fifo!=NULL)
   burn_source_free(o->fifo);

 free((char *) o);
 *objpt= NULL;
 return(1);
}


int isoburn_destroy_all(struct isoburn **objpt, int flag)
{
 struct isoburn *o,*n;

 o= *objpt;
 if(o==NULL)
   return(0);
 for(;o->prev!=NULL;o= o->prev);
 for(;o!=NULL;o= n) {
   n= o->next;
   isoburn_destroy(&o,0);
 }
 *objpt= NULL;
 return(1);
}


int isoburn_get_emulation_mode(struct isoburn *o, int *pt, int flag)
{
 *pt= o->emulation_mode;
 return(1);
}


int isoburn_get_target_volset(struct isoburn *o, struct iso_volset **pt,
                              int flag)
{
 *pt= o->target_volset;
 return(1);
}


int isoburn_get_prev(struct isoburn *o, struct isoburn **pt, int flag)
{
 *pt= o->prev;
 return(1);
}


int isoburn_get_next(struct isoburn *o, struct isoburn **pt, int flag)
{
 *pt= o->next;
 return(1);
}


int isoburn_link(struct isoburn *o, struct isoburn *link, int flag)
/*
  bit0= insert as link->prev rather than as link->next
*/
{

 /* >>> mutex */

 if(isoburn_list_start==NULL ||
    (isoburn_list_start==link && (flag&1)))
   isoburn_list_start= o;
 if(o->prev!=NULL)
   o->prev->next= o->next;
 if(o->next!=NULL)
   o->next->prev= o->prev;
 o->prev= o->next= NULL;
 if(link==NULL)
   return(1);
 if(flag&1) {
   o->next= link;
   o->prev= link->prev;
   if(o->prev!=NULL)
     o->prev->next= o;
   link->prev= o;
 } else {
   o->prev= link;
   o->next= link->next;
   if(o->next!=NULL)
     o->next->prev= o;
   link->next= o;
 }

 /* >>> end mutex */

 return(1);
}


int isoburn_count(struct isoburn *o, int flag)
/* flag: bit1= count from start of list */
{
 int counter= 0;

 if(flag&2)
   for(;o->prev!=NULL;o= o->prev);
 for(;o!=NULL;o= o->next)
   counter++;
 return(counter);
}


int isoburn_by_idx(struct isoburn *o, int idx, struct isoburn **pt, int flag)
/* flag: bit0= fetch first (idx<0) or last (idx>0) item in list
         bit1= address from start of list */
{
 int i,abs_idx;
 struct isoburn *npt;

 if(flag&2)
   for(;o->prev!=NULL;o= o->prev);
 abs_idx= (idx>0?idx:-idx);
 *pt= o;
 for(i= 0;(i<abs_idx || (flag&1)) && *pt!=NULL;i++) {
   if(idx>0)
     npt= o->next;
   else
     npt= o->prev;
   if(npt==NULL && (flag&1))
 break;
   *pt= npt;
 }
 return(*pt!=NULL);
}


int isoburn_find_by_drive(struct isoburn **pt, struct burn_drive *d, int flag)
{
 struct isoburn *o;

 *pt= NULL;
 for(o= isoburn_list_start;o!=NULL;o= o->prev)
   if(o->drive==d) {
     *pt= o;
     return(1);
   }
 return(0);
}

static
int isoburn_prepare_disc_aux(struct burn_drive *d, struct burn_disc **disc,
                             struct isoburn_source_opts *opts, int new_img)
{
 struct burn_source *wsrc;
 struct burn_session *session;
 struct burn_track *track;
 struct isoburn *o;
 struct ecma119_source_opts wopts;
 enum burn_disc_status state;
 int ret;

 ret= isoburn_find_emulator(&o, d, 0);
 if(ret<0 || o==NULL)
   return -1;
 o->wrote_well= 0; /* early end will be registered as failure */


 state = isoburn_disc_get_status(d);
 if (state != BURN_DISC_BLANK && state != BURN_DISC_APPENDABLE
     && (state != BURN_DISC_FULL || ! new_img)) {
   /* unsuitable status */
   return -2;
 }

 wopts.volnum = 0;
 wopts.level = opts->level;
 wopts.flags = opts->flags;
 wopts.relaxed_constraints = opts->relaxed_constraints;
 wopts.copy_eltorito = opts->copy_eltorito;
 wopts.no_cache_inodes = opts->no_cache_inodes;
 wopts.sort_files = opts->sort_files;
 wopts.default_mode = opts->default_mode;
 wopts.replace_dir_mode = opts->replace_dir_mode;
 wopts.replace_file_mode = opts->replace_file_mode;
 wopts.replace_uid = opts->replace_uid;
 wopts.replace_gid = opts->replace_gid;
 wopts.dir_mode = opts->dir_mode;
 wopts.file_mode = opts->file_mode;
 wopts.gid = opts->gid;
 wopts.uid = opts->uid;
 wopts.input_charset = opts->input_charset;
 wopts.ouput_charset = opts->ouput_charset;
 
 if (new_img) {
   wopts.ms_block = 0;
 } else {
   int lba, nwa;
   ret = isoburn_disc_track_lba_nwa(d, NULL, 0, &lba, &nwa);
   if (ret != 1)
     return -3;
   if (nwa == 0 && state == BURN_DISC_APPENDABLE) {
     /* invalid nwa */
     return -4;
   }
     
   wopts.ms_block = nwa;
 }

 wopts.src = o->src;
 wopts.overwrite = (new_img ? NULL : o->target_iso_head);
 wsrc = iso_source_new_ecma119(o->target_volset, &wopts);
 if (wsrc == NULL)
   return -1;
 
 /* TODO check return values for failure. propertly clean-up on error */

 o->fifo = burn_fifo_source_new(wsrc, 2048, 2000, 0);
 burn_source_free(wsrc);
 if (o->fifo == NULL) {
   fprintf(stderr, "Cannot attach fifo\n");
   return -1;
 }
 *disc = burn_disc_create();
 session = burn_session_create();
 burn_disc_add_session(*disc, session, BURN_POS_END);
 track = burn_track_create();
 burn_track_set_source(track, o->fifo);
 burn_session_add_track(session, track, BURN_POS_END);

 o->wrote_well= -1; /* neutral */
 return 1;
}

int isoburn_prepare_disc(struct burn_drive *d, struct burn_disc **disc,
                         struct isoburn_source_opts *opts)
{
	return isoburn_prepare_disc_aux(d, disc, opts, 0);
}

int isoburn_prepare_new_image(struct burn_drive *d, struct burn_disc **disc,
                         struct isoburn_source_opts *opts)
{
	return isoburn_prepare_disc_aux(d, disc, opts, 1);
}
