/* cdrecord.c
 * Copyright (C) 2004, 2005 Sylvain Cresto <scresto@gmail.com>
 *
 * This file is part of graveman!
 *
 * graveman! is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or
 * (at your option) any later version.
 * 
 * graveman! is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with program; see the file COPYING. If not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA. 
 * 
 * URL: http://www.nongnu.org/graveman/
 *
 */

#include "graveman.h"

/* communication avec cdrecord */

#define SCANDEVICE "scsibus"
#define CDRECORD_NOSUCHFILE "No read access for"
#define CDRECORD_COPY "Track"
#define CDRECORD_OF	"of"
#define CDRECORD_MB	"MB"
#define CDRECORD_FIFO "fifo"
#define CDRECORD_BUF "buf"
#define CDRECORD_STATUS "fifo was "
#define CDRECORD_ERRORDISK "No disk / Wrong disk"
#define CDRECORD_INAPPROPRIATE "Inappropriate audio coding"
#define CDRECORD_INPUTOUTPUT_ERROR "Input/output error."
#define CDRECORD_FIXATING "Fixating..."
#define CDRECORD_BLANKING "Blanking "
#define CDRECORD_BAD_RECORDER "Sorry, no CD/DVD-Recorder or unsupported CD/DVD-Recorder found"
#define CDRECORD_CANNOT_OPEN "Cannot open"

/* support du materiel */
#define CDRECORD_NOT_READ_CD "Does not read CD-R media"
#define CDRECORD_READ_CD "Does read CD-R media"
#define CDRECORD_NOT_WRITE_CDR "Does not write CD-R media"
#define CDRECORD_WRITE_CDR "Does write CD-R media"
#define CDRECORD_NOT_WRITE_CDRW "Does not write CD-RW media"
#define CDRECORD_WRITE_CDRW "Does write CD-RW media"
#define CDRECORD_READ_DVD "Does read DVD-ROM media"
#define CDRECORD_WRITE_DVD "Does write DVD-R media"
#define CDRECORD_NOT_WRITE_DVD "Does not write DVD-R media"
#define CDRECORD_NOT_WRITE_DUMMY "Does not support test writing"
#define CDRECORD_MAX_CDREAD_SPEED "Maximum read  speed:"
#define CDRECORD_MAX_CDREAD_SPEED_CD "(CD"
#define CDRECORD_MAX_CDREAD_SPEED_DVD " DVD "
#define CDRECORD_BURNFREE "Prepare writer to use BURN-Free technology"

/* effacer un cdrw ou cloturer un cdr */
#define CDRECORD_BLANKING_TIME "Blanking time:"
#define CDRECORD_CANNOT_BLANK "Cannot blank disk, aborting"
#define CDRECORD_INCOMPATIBLE_MEDIUM "cannot format medium - incmedium"
#define CDRECORD_FIXATING_TIME "Fixating time:"


/* definition de tous les type de bus */
Tsearchdrive listesearchdrives[] = {
    { "", "", "SCSI", 110 }, /* scsi (sg) */
#if LINUX_IDE /* pure ide devices with linux */
    { "dev=/dev/hda", "/dev/hda", "IDE", 100 },
    { "dev=/dev/hdb", "/dev/hdb", "IDE", 100 },
    { "dev=/dev/hdc", "/dev/hdc", "IDE", 100 },
    { "dev=/dev/hdd", "/dev/hdd", "IDE", 100 },
    { "dev=/dev/hde", "/dev/hde", "IDE", 100 },
    { "dev=/dev/hdf", "/dev/hdf", "IDE", 100 },
    { "dev=/dev/hdg", "/dev/hdg", "IDE", 100 },
    { "dev=/dev/hdh", "/dev/hdh", "IDE", 100 },
    { "dev=/dev/hdi", "/dev/hdi", "IDE", 100 },
    { "dev=/dev/hdj", "/dev/hdj", "IDE", 100 },
#endif
#if LINUX_SCSI  /* SCSI alias with linux */
    { "dev=/dev/scd0", "/dev/scd0", "SCSI-D", 90 },
    { "dev=/dev/scd1", "/dev/scd1", "SCSI-D", 90 },
    { "dev=/dev/scd2", "/dev/scd2", "SCSI-D", 90 },
    { "dev=/dev/scd3", "/dev/scd3", "SCSI-D", 90 },
    { "dev=/dev/scd4", "/dev/scd4", "SCSI-D", 90 },
    { "dev=/dev/scd5", "/dev/scd5", "SCSI-D", 90 },
    { "dev=/dev/scd6", "/dev/scd6", "SCSI-D", 90 },
    { "dev=/dev/sr0", "/dev/sr0",  "SCSI-S", 92 },
    { "dev=/dev/sr1", "/dev/sr1",  "SCSI-S", 92 },
    { "dev=/dev/sr2", "/dev/sr2",  "SCSI-S", 92 },
    { "dev=/dev/sr3", "/dev/sr3",  "SCSI-S", 92 },
    { "dev=/dev/sr4", "/dev/sr4",  "SCSI-S", 92 },
    { "dev=/dev/sr5", "/dev/sr5",  "SCSI-S", 92 },
    { "dev=/dev/sr6", "/dev/sr6",  "SCSI-S", 92 },
    { "dev=/dev/sg0", "/dev/sg0",  "SCSI-G", 94 },
    { "dev=/dev/sg1", "/dev/sg1",  "SCSI-G", 94 },
    { "dev=/dev/sg2", "/dev/sg2",  "SCSI-G", 94 },
    { "dev=/dev/sg3", "/dev/sg3",  "SCSI-G", 94 },
    { "dev=/dev/sg4", "/dev/sg4",  "SCSI-G", 94 },
    { "dev=/dev/sg5", "/dev/sg5",  "SCSI-G", 94 },
    { "dev=/dev/sg6", "/dev/sg6",  "SCSI-G", 94 },
    { "dev=/dev/sga", "/dev/sga",  "SCSI-g", 96 },
    { "dev=/dev/sgb", "/dev/sgb",  "SCSI-g", 96 },
    { "dev=/dev/sgc", "/dev/sgc",  "SCSI-g", 96 },
    { "dev=/dev/sgd", "/dev/sgd",  "SCSI-g", 96 },
    { "dev=/dev/sge", "/dev/sge",  "SCSI-g", 96 },
    { "dev=/dev/sgf", "/dev/sgf",  "SCSI-g", 96 },
    { "dev=/dev/sgg", "/dev/sgg",  "SCSI-g", 96 },
#endif
    { "dev=ATA:", "ATA:", "ATA", 60 }, /* ide sg */
    { "dev=ATAPI:", "ATAPI:", "ATAPI", 40 }, /* ide */
    { NULL, NULL, NULL, 0}
  };

gboolean cdrecord_grave_callback(GIOChannel *Astd, GIOCondition Acond, gpointer Adata);

static gchar *get_title(gint Anbrgravure, gint Acurcd, gboolean Adosimul)
{
  gchar *Ltxt = NULL;
  if (Anbrgravure==1) {
    if (!Adosimul) {
      Ltxt = g_strdup(_("Writing in progress..."));
    } else {
      Ltxt = g_strdup(_("Simulated CD writing in progress..."));
    }
  } else {
    if (!Adosimul) {
      Ltxt = g_strdup_printf(_("Writing CD %d/%d in progress..."), Acurcd, Anbrgravure);
    } else {
      Ltxt = g_strdup_printf(_("Simulated CD writing %d/%d in progress..."), Acurcd, Anbrgravure);
    }
  }

  return Ltxt;
}

/* recherche des lecteurs */
gboolean cdrecord_extractlecteur(gchar *Achaine, gchar **Adev, gchar **Amarque, gchar **Amodel,
  gchar **Aquoi)
{
  gchar *s = Achaine;
  gint Lnbnbr = 0;
  gint Lnbvirg = 0;
  gint Lsize = 0;

  while (isdigit(*s) || *s==',') {
    if (*(s++)==',') { Lnbvirg++; } else { Lnbnbr++; }
  }
  if (!*s) return FALSE;
  *(s++)=0;
  g_strstrip(s);
  
  if (*s && Lnbvirg == 2 && Lnbnbr>2) {
    gchar **Linfo = g_strsplit(s, "'", 10);
    Lsize = sc_strcountv(Linfo);
    sc_strstripv(Linfo);
    
    if (Lsize>=6) {
      *Adev = g_strdup(Achaine);
      *Amarque = _UTF8(Linfo[1]);
      *Amodel = _UTF8(Linfo[3]);
      *Aquoi = _UTF8(Linfo[6]);
    }
    
    g_strfreev(Linfo);    
  }
  return Lsize >= 6 ? TRUE : FALSE;
}

/* callback detection des lecteurs et graveurs */
gboolean scan_callback(GIOChannel *Astd, GIOCondition Acond, gpointer Adata)
{
  GIOStatus Lstatus;
  Tgrave *Lg = (Tgrave *)Adata;
  gint *Lcont = (gint *)sc_grave_get_data(Lg, "cont");
  Tsearchdrive *Ldrivedesc = (Tsearchdrive *)sc_grave_get_data(Lg, "drivedesc");
  gchar *Lbuffer = NULL;
  gchar *Lmodel, *Lmarque, *Ldev, *Lquoi;
  
  /* fin du callback lorsque l'on recoi un signal comme quoi le pipe est ferme */
  if (Acond == G_IO_HUP || Acond == G_IO_ERR) {
    *Lcont = 2;
    return FALSE;
  }
  
  Lstatus = g_io_channel_read_line(Astd, &Lbuffer, NULL, NULL, NULL);  
  if (!Lbuffer) return TRUE;
    
  g_strstrip(Lbuffer);  
_DEB("scan lecteur = %s\n", Lbuffer);
  if (strstr(Lbuffer, CDRECORD_CANNOT_OPEN)) {
    *Lcont = 2;
    return FALSE;
  }

  if (cdrecord_extractlecteur(Lbuffer, &Ldev, &Lmarque, &Lmodel, &Lquoi) == TRUE) {

    matos_insert_or_update_drive(Ldev, Lmarque, Lmodel, Lquoi, Ldrivedesc);

    _DEB("oui un lecteur");
    
    g_free(Lquoi);
    g_free(Lmodel);
    g_free(Lmarque);
    g_free(Ldev);
  }
    
  g_free(Lbuffer);
  
  return TRUE;
}

/* scan des lecteurs */
gboolean cdrecord_scan_for_drives(Tgrave *Ag, GError **Aerror)
{
  gchar **Lcmd;
  gchar *Lcommandline;
  gint Lpid, g_out, g_err, Lnbrarg;
  gboolean Lbolstatus;
  GIOChannel *Lcom;
  gboolean Lstatus;
  guint Lcomevent;
  Tsearchdrive *Lcurentry;
  gint *Lcont = (gint *)sc_grave_get_data(Ag, "cont");
  gboolean *Labort = (gboolean *)sc_grave_get_data(Ag, "gabort");

  free_drives(FALSE);
  for (Lcurentry = listesearchdrives; Lcurentry->detectline && *Labort == FALSE; Lcurentry++) {

    Lcommandline = g_strdup_printf("%s -scanbus %s", conf_get_string("cdrecord"), Lcurentry->detectline);
  _DEB("commande =%s\n", Lcommandline);
    Lstatus = g_shell_parse_argv(Lcommandline, &Lnbrarg, &Lcmd, Aerror);
    g_free(Lcommandline);
    if (Lstatus == FALSE) return FALSE;

    *Lcont = 3;
    sc_grave_set_data(Ag, Lcurentry, "drivedesc");

    Lbolstatus = g_spawn_async_with_pipes(NULL, Lcmd, NULL, /* env argument */
      (GSpawnFlags ) (0),
      NULL, NULL, &Lpid, NULL, &g_out, &g_err, Aerror);
    g_strfreev(Lcmd);
  
    if (Lbolstatus == FALSE) {
      g_warning("ERROR EXECUTION !\n");
      return FALSE;
    }

    Lcom = g_io_channel_unix_new( g_out );
    g_io_channel_set_encoding (Lcom, NULL, NULL);
    g_io_channel_set_flags( Lcom, G_IO_FLAG_NONBLOCK, NULL );
    Lcomevent = g_io_add_watch (Lcom, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
  					     scan_callback,
					     Ag); 
    while (*Lcont==3) {
      gtk_main_iteration();    
    }

    g_source_remove(Lcomevent);
      
    g_io_channel_shutdown(Lcom, FALSE, NULL);
    g_io_channel_unref(Lcom);
    g_spawn_close_pid(Lpid);
    sc_grave_del_data(Ag, "drivedesc");
  }

  return TRUE;
}

/* mise a jour capacité du lecteur */
gboolean cdrecord_maj_drive_info(Tdriveinfo *Adrive, gchar *Adev, GError **Aerror)
{
  gchar *Lcommandline, *Lout = NULL, *Lerr = NULL;
  gchar *scd = NULL, *sdvd = NULL, *f;
  gint Lexit = 0;
  gboolean Lstatus;

  Lcommandline = g_strdup_printf("%s -prcap dev=%s", conf_get_string("cdrecord"),
      Adev ? Adev : DRIVE_DEV(Adrive));

_DEB("==================%s\n", Lcommandline);  
  Lstatus = g_spawn_command_line_sync(Lcommandline, &Lout, &Lerr, &Lexit, Aerror);
  g_free(Lcommandline);
  if (Lstatus == FALSE || !Lout || !Lerr || !*Lout) {
    g_free(Lout);
    g_free(Lerr);
    return FALSE;
  }

  if (strstr(Lout, CDRECORD_READ_CD)) Adrive->type |= _READ_CDR;
  if (strstr(Lout, CDRECORD_WRITE_CDR)) Adrive->type |= _WRITE_CDR;
  if (strstr(Lout, CDRECORD_WRITE_CDRW)) Adrive->type |= _WRITE_CDRW;
  if (strstr(Lout, CDRECORD_READ_DVD)) Adrive->type |= _READ_DVD;
  if (strstr(Lout, CDRECORD_WRITE_DVD)) Adrive->type |= _WRITE_DVD;

  /* vitesse de lecture maximum CD */
  if ((scd=strstr(Lout, CDRECORD_MAX_CDREAD_SPEED))) {
    scd+=strlen(CDRECORD_MAX_CDREAD_SPEED);
    if ((scd=strstr(scd, CDRECORD_MAX_CDREAD_SPEED_CD))) {
      scd=ltrim(scd+strlen(CDRECORD_MAX_CDREAD_SPEED_CD));
    }
    f=scd;
    while (isdigit(*f)) f++;
    *(f++)=0;

    if ((sdvd=strstr(f, CDRECORD_MAX_CDREAD_SPEED_DVD))) {
      sdvd=ltrim(sdvd+strlen(CDRECORD_MAX_CDREAD_SPEED_DVD));
    }
    f=sdvd;
    while (isdigit(*f)) f++;
    *(f++)=0;
  }
  
  g_free(Lout);
  g_free(Lerr);

  if (!scd) return FALSE;

  Adrive->vitesse = atoi(scd);  /* vitesse de lecture ou de gravure maximum */
  Adrive->vitessedvd = sdvd ? atoi(sdvd) : -1;
  _DEB("== le type => %d\n", Adrive->type);
  _DEB("== donc la vitesse de lecture => %d\n", Adrive->vitesse  );


  /* ok maintenant on regarde si le lecteur supporte le burnfree, si oui on l'utilisera
   * lors des gravures */
  Lcommandline = g_strdup_printf("%s -checkdrive dev=%s driveropts=help", conf_get_string("cdrecord"),
      Adev ? Adev : DRIVE_DEV(Adrive));
_DEB("==================%s\n", Lcommandline);  
  Lstatus = g_spawn_command_line_sync(Lcommandline, &Lout, &Lerr, &Lexit, Aerror);
  g_free(Lcommandline);
  if (Lstatus == TRUE) {
    if (strstr(Lout, CDRECORD_BURNFREE)) Adrive->type |= _BURN_FREE;
  }
  g_free(Lout);
  g_free(Lerr);

  return TRUE;
}

/* recherche les parametres supllementaires a passer a cdrecord */
gchar *burn_data_getextrapara(Tgrave *Ag)
{
  gchar *Lformat = get_combo_value(sc_grave_get_widget(Ag, "dataformat"));
  gchar *Lmodburn = get_combo_value(sc_grave_get_widget(Ag, "datamodburn"));
  gchar *Lreturn;

  Lreturn = g_strdup_printf("-%s %s%s", Lformat, Lmodburn && *Lmodburn ? "-" : "", Lmodburn);
  
  g_free(Lmodburn); g_free(Lformat);

  return Lreturn;
}

static gchar *get_blank_type(Tgrave *Ag, gint Atypemedia)
{
  gboolean *Luseautoblank = (gboolean *) sc_grave_get_data(Ag, "useautoblank");

  if (!*Luseautoblank || (!(Atypemedia & _MEDIA_CDRW))) return g_strdup("");

  return g_strdup_printf("blank=%s", conf_get_boolean("fastblank") == TRUE ? "fast" : "all");
}

/* copie d'une image iso */
gboolean burn_from_image(gchar *Aop, Tgrave *Ag, GError **Aerror) {
  gchar **Lcmd;
  gchar *Lcommandline, *Ltxt;
  GIOChannel *Lcom, *Lcomerr;
  guint Lcomevent, Lcomerrevent;
  gint *Lcont = (gint *) sc_grave_get_data(Ag, "cont");
  gboolean *Labort = (gboolean *) sc_grave_get_data(Ag, "gabort");
  GtkWindow *Lwindow = GTK_WINDOW(sc_grave_get_data(Ag, "window_burn"));
  gboolean Lsimul = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sc_grave_get_widgetv(Ag, "%ssimul", Aop)));
  gint Lnbrgravure = gtk_spin_button_get_value(GTK_SPIN_BUTTON(sc_grave_get_widgetv(Ag, "nbrcd%s", Aop)));  
  gboolean *Loperationstatus = (gboolean *)sc_grave_get_data(Ag, "operationstatus");
  gboolean Lwanteject = conf_get_boolean("eject");
  gchar *Liso = (gchar *)sc_grave_get_data(Ag, "iso"); /* image iso */
  gint *Lpid = (gint *) sc_grave_get_data(Ag, "pid");
  gboolean Ldosimul;
  GtkWidget *Lvitesse = sc_grave_get_widgetv(Ag, "dst%sspeed", Aop);
  GtkLabel *Ltitle = GTK_LABEL(sc_grave_get_data(Ag, "gravetitle"));
  GtkToggleButton *Lbtnnotfix = GTK_TOGGLE_BUTTON(sc_grave_get_widgetv(Ag, "%snotfix", Aop));
  gboolean Lnotfix = Lbtnnotfix ? gtk_toggle_button_get_active(Lbtnnotfix) : FALSE;
  gchar *Lextrapara = sc_grave_get_data(Ag, "extrapara");
  gboolean Lmulti = FALSE;
  gint *Ldone = (gint *)sc_grave_get_data(Ag, "done"); /* fais */
  gint Lcurcd;
  gint Lnbrpass=1;
  gchar *Lbufvitesse, *Lblank;
  gchar Llasttrack[_BUF_SIZE] = "00";
  gint g_out, g_err, Lnbrarg;
  gboolean Lstatus = FALSE;
  gboolean Leject = FALSE;
  GtkToggleButton *Ltbtn = NULL;
  Tdriveinfo *Ldevice = NULL;
  gchar Lgravident[_BUF_SIZE];
  gint Lmediadetect1 = _MEDIA_CDRW;

  g_snprintf(Lgravident, sizeof(Lgravident)-1, "dst%scombo", Aop);

  Ldevice = matos_get_drive_info(Ag, Lgravident);
  
  Lbufvitesse = get_combo_value(Lvitesse);

  if ((Ltbtn = GTK_TOGGLE_BUTTON(sc_grave_get_widgetv(Ag, "%smulti", Aop))))
    Lmulti =gtk_toggle_button_get_active(Ltbtn);

  _DEB("on veut graver %d cd simul(%d)", Lnbrgravure, Lsimul ? 1 : 0);

  for (Lcurcd=1; Lcurcd<= Lnbrgravure;
      ((Lsimul && Lnbrpass>1) || (!Lsimul)) ? ( Lcurcd++, Lnbrpass=1 ) : ( Lnbrpass++ )) {

    Ldosimul = (Lsimul && Lnbrpass==1);
    if (Lcurcd > 1 && !Ldosimul) {
      /* copie sur un nouveau cd, on demande a l'utilisateur d'inserer le
       * nouveau cd vierge */
      GSList *Llmediarequis1 = sc_grave_get_data(Ag, "mediarequis1");
      gint *Lmediatitle1 = sc_grave_get_data(Ag, "mediatitle1");
      gchar *Ltxt = sc_grave_get_data(Ag, "medialabel");
      gboolean Lstatus;
      Tgrave *Ldialoghash;
      GtkWidget *Lconfirm;
      gint Lrep;

      eject_cd(DRIVE_DEV(Ldevice), NULL);
      Lstatus = waiting_for_user(*Lmediatitle1, Ag, Llmediarequis1 , &Lmediadetect1, Ldevice);
          
      if (Lstatus==FALSE) {
        /* si c'est non alors on arrete */
        *Labort = TRUE; Lstatus = TRUE; break;
      } 
      
      Ldialoghash = create_dialog_confirm_operation(GTK_WIDGET(Lwindow), Ltxt,
       DRIVE_CDRW_WRITER(Ldevice) == TRUE &&
        ((Lmediadetect1 & _MEDIA_CDRW) && (Lmediadetect1 & _MEDIA_NOBLANK)) ?
        _("Blank CD-RW before writing") : NULL, conf_get_boolean("autoblank"));

      Lconfirm = sc_grave_get_data(Ldialoghash, "window");
      Lrep = gtk_dialog_run(GTK_DIALOG(Lconfirm));

      if (Lrep != GTK_RESPONSE_YES) {
        /* si c'est non alors on arrete */
        *Labort = TRUE; Lstatus = TRUE;
      } else if (DRIVE_CDRW_WRITER(Ldevice) == TRUE) {
        gboolean *Luseautoblank = (gboolean *) sc_grave_get_data(Ag, "useautoblank");
        GtkCheckButton *Labcheck = GTK_CHECK_BUTTON(sc_grave_get_widget(Ldialoghash, "checkbox"));
        *Luseautoblank = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(Labcheck));
      }
      gtk_widget_destroy(Lconfirm);

      if (*Labort == TRUE) break;
    }

    /* faut il ejecter le CD-R apres l'operation ?
     * oui si l'utilisateur a cocher la case "ejecter le cd" ou
     * si il faut realiser d'autre gravure sur d'autres CD-R */
    Leject = ((Lcurcd > 1 && (Lwanteject || Lcurcd<Lnbrgravure)) || (Lcurcd == 1 && !Ldosimul && Lwanteject));

    *Loperationstatus = FALSE;

    _DEB("gravure du cd [%d]", Lcurcd);
    Ltxt = get_title(Lnbrgravure, Lcurcd, Ldosimul);
    gtk_label_set_text(Ltitle, Ltxt);
    g_free(Ltxt);

    Lblank = get_blank_type(Ag, Lmediadetect1);

    /* try to umount device before device access */
    matos_umount_device(Ldevice, NULL);

    Lcommandline = g_strdup_printf("%s dev=%s -v gracetime=2 %s%s %s %s %s %s %s %s %s %s '%s' %s",
        conf_get_string("cdrecord"), DRIVE_DEV(Ldevice),
        *Lbufvitesse != '0' ? "speed=" : "", *Lbufvitesse != '0' ? Lbufvitesse : "",  
        Ldosimul ? " -dummy" : "",  /* simulation ? */
        Ldosimul || Lnotfix ? " -nofix" : "", /* fixer le cd apres ecriture ? */
        Lmulti ? " -multi" : "",  /* multi-session */
        Leject ? "-eject" : "", /* ejecter le cd apres l'operation */
        Lblank,
        Lextrapara && *Lextrapara ? Lextrapara : "",  /* parametre supplementaire tel que le mode d'ecriture */
        conf_get_boolean("overburn") ? "-overburn" : "",
        conf_get_string("cdrecordpara"),
        Liso,
        DRIVE_BURN_FREE(Ldevice) ? "driveropts=burnfree" : ""
        );
    g_free(Lblank);
  
  _DEB("execution [%s]\n", Lcommandline);

    Lstatus = g_shell_parse_argv(Lcommandline, &Lnbrarg, &Lcmd, Aerror);
    g_free(Lcommandline);
    if (Lstatus == FALSE) {
      break;
    }

    Lstatus = g_spawn_async_with_pipes(NULL, Lcmd, NULL, /* env argument */
        (GSpawnFlags ) (G_SPAWN_DO_NOT_REAP_CHILD),
        NULL, NULL, Lpid, NULL, &g_out, &g_err, Aerror);
    g_strfreev(Lcmd);

    if (Lstatus == FALSE) {
      g_warning("ERROR EXECUTION !\n");
      break;
    }

    *Lcont = 1;
    sc_grave_set_data(Ag, &Llasttrack, "lasttrack");
    Lcom = g_io_channel_unix_new( g_out );
    g_io_channel_set_encoding (Lcom, NULL, NULL);
    g_io_channel_set_buffered(Lcom, FALSE);
    g_io_channel_set_flags( Lcom, G_IO_FLAG_NONBLOCK, NULL );
    Lcomevent = g_io_add_watch (Lcom, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                      cdrecord_grave_callback, Ag);
  
    Lcomerr = g_io_channel_unix_new( g_err );
    g_io_channel_set_encoding (Lcomerr, NULL, NULL);
    g_io_channel_set_buffered(Lcomerr, FALSE);
    g_io_channel_set_flags( Lcomerr, G_IO_FLAG_NONBLOCK, NULL );
    Lcomerrevent = g_io_add_watch (Lcomerr, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                      cdrecord_grave_callback, Ag); 
    
    while (*Lcont > 0 && *Labort == FALSE) {
      gtk_main_iteration();    
    }
    exit_prog(*Lpid, FALSE, Aerror, NULL);

    g_source_remove(Lcomevent);
    g_source_remove(Lcomerrevent);
      
    g_io_channel_shutdown(Lcomerr, FALSE, NULL);
    g_io_channel_unref(Lcomerr);  
    g_io_channel_shutdown(Lcom, FALSE, NULL);
    g_io_channel_unref(Lcom);
    g_spawn_close_pid(*Lpid);
    *Lpid = 0;

    sc_grave_del_data(Ag, "lasttrack");

    if (*Aerror) {
      _DEB("IL Y A UNE ERREUR !!");
      Lstatus = FALSE;
      break; 
    }

    if (*Loperationstatus == FALSE) {
      /* cdrecord n'a pas reussi a grave le cd mais on n'a intercepte aucune erreur ! argh!
       * dans tous les cas ce n'est pas normal, on genere une erreur */
      g_set_error(Aerror, GRAVEMAN_ERROR, _ERR_UNKNOWN_ERROR, 
        _("Communication error with cdrecord. Check that you have access to cdrecord release 2.0 !"));
      Lstatus = FALSE;
      break;
    }

    if (Ldosimul) {
      /* fin de la simulation, tout s'est apparement bien passe
       * on demande confirmation avent de commencer la vrai gravure */
      gint Lrep;
      GtkWidget *Lconfirm = gtk_message_dialog_new(Lwindow,
                                          GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
                                          GTK_BUTTONS_YES_NO, 
                              _("Simulation successful. Do you want to write the CD for real?"));
      Lrep = gtk_dialog_run(GTK_DIALOG(Lconfirm));
      gtk_widget_destroy(Lconfirm);
      if (Lrep == GTK_RESPONSE_NO) {
        /* si c'est non alors on arrete */
        *Labort = TRUE;
        Lstatus = TRUE;
        break;
      }
    }

    (*Ldone)++;
    strcpy(Llasttrack, "00");
  }

  g_free(Lbufvitesse);
  
  return Lstatus;
}

/* mise a jour du label d'avancement */
void maj_audio_title(GtkLabel *Alabel, gchar *Apiste, gchar *Avitesse, gdouble Amoafaire, gdouble Amofait)
{
  gchar *Ltxt = NULL;
  if (Amoafaire != 0) {
    Ltxt = g_strdup_printf(_("Writing track %s - %.0f MB of %.0f MB at %s"), Apiste ? Apiste : "-",
        Amofait, Amoafaire, Avitesse ? Avitesse : "-");
  } else {
    Ltxt = g_strdup_printf(_("Writing track %s at %s"), Apiste ? Apiste : "-", Avitesse ? Avitesse : "-");
  }

  gtk_label_set_text(Alabel, Ltxt);
  g_free(Ltxt);
}

/* callback appele lorsque cdrecord grave les pistes */
gboolean cdrecord_grave_callback(GIOChannel *Astd, GIOCondition Acond, gpointer Adata)
{
  GIOStatus Lstatus;
  Tgrave *Lg = (Tgrave *)Adata;
  gchar Lbuffer[_BUF_SIZE];
  gint *Lcont = (gint *) sc_grave_get_data(Lg, "cont"); /* on traite encore des donnees ? */
  GError **Lerreur = (GError **) sc_grave_get_data(Lg, "gerror"); /* pointeur erreur */
  gint *Ltodo = (gint *) sc_grave_get_data(Lg, "todo"); /* nombre de piste a traiter */
  gint *Ldone = (gint *) sc_grave_get_data(Lg, "done"); /* nombre de piste deja traite */
  gchar *Llasttrack = (gchar *) sc_grave_get_data(Lg, "lasttrack"); /* precedente piste traite */
  gchar *f, *e;
  gchar *Ltxt;
  gchar Lsbuf[200];
  gdouble Ltotaldone = 1, Ltotaltodo, Lfifo, Lpct, Lgbuf = 0, Ltava;
  gsize Llu = 0;
  gchar *Ltracknum;

  GtkLabel *Ltitle = GTK_LABEL(sc_grave_get_data(Lg, "gravetitle"));
  GtkProgressBar *Lprogressbar = GTK_PROGRESS_BAR(sc_grave_get_data(Lg, "pb_total"));
  GtkProgressBar *Lprogressbar2 = GTK_PROGRESS_BAR(sc_grave_get_data(Lg, "pb_step"));
  GtkProgressBar *LprogressbarFifo = GTK_PROGRESS_BAR(sc_grave_get_data(Lg, "pb_fifo"));
  GtkProgressBar *LprogressbarBuffer = GTK_PROGRESS_BAR(sc_grave_get_data(Lg, "pb_buffer"));
  
  /* fin du callback lorsque l'on recoi un signal comme quoi le pipe est ferme */
  if (Acond == G_IO_HUP || Acond == G_IO_ERR) {
    (*Lcont) = 0;
    return FALSE;
  }
  
  *Lbuffer = 0;
  Lstatus = g_io_channel_read_chars(Astd, Lbuffer, _BUF_SIZE-1, &Llu, NULL);  
  if (!*Lbuffer || Lstatus == G_IO_STATUS_ERROR || Lstatus == G_IO_STATUS_AGAIN) {
    return FALSE;
  }
  Lbuffer[Llu]=0;
_DEB("===>%s", Lbuffer);

  if ((f=strstr(Lbuffer, CDRECORD_COPY))) {
     /* copie en cours */

    /* champ deja grave */
    f=f+strlen(CDRECORD_COPY);
    Ltracknum = f;
    if (!(f=strchr(f, ':'))) return TRUE;
    *(f++)=0;
    g_strstrip(Ltracknum);
    
    if (!(e = strstr(f, CDRECORD_OF))) return TRUE;
    

    *e=0; e=e+strlen(CDRECORD_OF);
    Ltotaldone = atof(ltrim(f));

    /* champ total a graver */
    f=e;
    if (!(e=strstr(f, CDRECORD_MB))) {
      maj_audio_title(Ltitle, Ltracknum, NULL, 0, Ltotaldone);
      return TRUE;
    }
    *e=0; e=e+strlen(CDRECORD_MB);
    Ltotaltodo = atof(ltrim(f));

    /* champ fifo */
    if (!(f=strstr(e, CDRECORD_FIFO))) {
      maj_audio_title(Ltitle, Ltracknum, NULL, Ltotaltodo, Ltotaldone);
      return TRUE;
    }
    f=f+strlen(CDRECORD_FIFO);

    if ((e = strchr(++f, ')'))) {
      *e++=0;
    } else {
      e = f;
    }
    Lfifo = atof(ltrim(f));

    /* champ buf */
    if (!(f=strstr(e, CDRECORD_BUF))) {
      maj_audio_title(Ltitle, Ltracknum, NULL, Ltotaltodo, Ltotaldone);
      return TRUE;
    }
    f=f+strlen(CDRECORD_BUF);
				
    if ((e = strchr(++f, ']'))) {
      *e++=0;
      Lgbuf = atof(ltrim(f));
    } else {
      e = f;
    }

    e=ltrim(e);
    if ((f=strchr(e, 'x'))) {
      *(++f)=0;
      maj_audio_title(Ltitle, Ltracknum, e, Ltotaltodo, Ltotaldone);
    }

    /* barres de progression */
    /* avancement operation */
    Lpct = (1.0/Ltotaltodo) * Ltotaldone;
    maj_progress(Lprogressbar2, Lpct);

    /* avancement total */
    if (*Ltracknum && (!*Llasttrack || strcmp(Llasttrack, Ltracknum))) {
      if (strcmp(Llasttrack, "00")) *Ldone=(*Ldone)+1;
      g_strlcpy(Llasttrack, Ltracknum, _BUF_SIZE - 1);
    }
    Ltava = (1.0/ (*Ltodo)) * ((*Ldone)-1+Lpct);
    maj_progress(Lprogressbar, Ltava);

    /* barre fifo */
    g_snprintf(Lsbuf, sizeof(Lsbuf)-1, "%.0f%%", Lfifo);
    gtk_progress_bar_set_fraction(LprogressbarFifo, Lfifo * 0.01);
    gtk_progress_bar_set_text(LprogressbarFifo, Lsbuf);

    /* barre buffer */
    g_snprintf(Lsbuf, sizeof(Lsbuf)-1, "%.0f%%", Lgbuf);
    gtk_progress_bar_set_fraction(LprogressbarBuffer, Lgbuf * 0.01);
    gtk_progress_bar_set_text(LprogressbarBuffer, Lsbuf);

  } else if (strstr(Lbuffer, CDRECORD_FIXATING)) {
    /* cloture du cd */
    gtk_label_set_text(Ltitle, _("Fixating..."));
  } else if (strstr(Lbuffer, CDRECORD_BLANKING)) {
    /* blanking disk */
    gtk_label_set_text(Ltitle, _("Blanking CD-RW..."));
  } else if ((f=strstr(Lbuffer, CDRECORD_STATUS))) {
    gboolean *Lstatus = (gboolean *) sc_grave_get_data(Lg, "operationstatus");
/* bug 11803
 * cause des problems apparements, a tester .. 
    f=f+strlen(CDRECORD_STATUS);
     fin de la gravure 
    if (*f != '0') {
      g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_BUF_EMPTY, _("Error writing tracks, buffer was empty !"));
      return FALSE;
    } */
    /* ok tout s'est bien passe ! */
    *Lstatus = TRUE;
  } else if (strstr(Lbuffer, CDRECORD_INPUTOUTPUT_ERROR)) {
    /* erreur entre sortie */
    g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_NO_CD, _("Error writing CD !"));
    (*Lcont) = 0;
    return FALSE;
  } else if (strstr(Lbuffer, CDRECORD_CANNOT_BLANK)) {
    g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_CANNOT_BLANK, _("Cannot blank disk, aborting."));
    (*Lcont) = 0;
    return FALSE;
  } else if ((strstr(Lbuffer, CDRECORD_ERRORDISK)) || (strstr(Lbuffer, CDRECORD_INCOMPATIBLE_MEDIUM))) {
    /* erreur pas de cd vierge */
    g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_NO_CD, _("Error, a CD-R/CD-RW is required in the cd recorder !"));
    (*Lcont) = 0;
    return FALSE;
  } else if (strstr(Lbuffer, CDRECORD_BAD_RECORDER)) {
    /* erreur pas de cd vierge */
    g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_BAD_RECORDER, _("CD recorder unsupported !"));
    (*Lcont) = 0;
    return FALSE;
  } else if ((strstr(Lbuffer, CDRECORD_INAPPROPRIATE))) {
    /* erreur fichier audio non compatible */
    g_snprintf(Lsbuf, sizeof(Lsbuf)-1, _("Error with track %.0f: inappropriate audio coding !"), Ltotaldone);
    g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_INAPPROPRIATE_DATA, Lsbuf);
    (*Lcont) = 0;
    return FALSE;
  } else if ((f=strstr(Lbuffer, CDRECORD_NOSUCHFILE))) { 
    /* erreur fichier image iso source introuvable ! */
    g_snprintf(Lsbuf, sizeof(Lsbuf)-1, _("Cannot open iso image !"));
    g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_INAPPROPRIATE_DATA, Lsbuf);
    (*Lcont) = 0;
    return FALSE;
  } else if ((f=strstr(Lbuffer, conf_get_string("cdrecord")))) {
    f=f+strlen(conf_get_string("cdrecord"))+1;
    if (*(f++)==':') {
      /* erreur cdrecord */
      Ltxt = _UTF8(f);
      g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_INAPPROPRIATE_DATA, Ltxt);
      g_free(Ltxt);
      (*Lcont) = 0;
      return FALSE;
    }
  } 

  return TRUE;
}

gboolean gravepiste(Tgrave *Ag, GError **Aerror) {
  GtkWindow *Lwindow = GTK_WINDOW(sc_grave_get_data(Ag, "window_burn"));
  GtkLabel *Ltitle = GTK_LABEL(sc_grave_get_data(Ag, "gravetitle"));
  GtkWidget *Lvitesse = sc_grave_get_widget(Ag, "dstaudiospeed");
  gchar *Lrepertoire = (gchar *)sc_grave_get_data(Ag, "tmpdir");
  gboolean *Loperationstatus = (gboolean *)sc_grave_get_data(Ag, "operationstatus");
  gboolean *Labort = (gboolean *) sc_grave_get_data(Ag, "gabort");
  gchar *Lbuftitle = NULL;
  gchar **Lcmd;
  gchar *Lcommandline;
  GIOChannel *Lcom, *Lcomerr;
  guint Lcomevent, Lcomerrevent;
  gint *Lcont = (gint *) sc_grave_get_data(Ag, "cont"); /* on traite encore des donnees ? */
  /* nombre d'element total */
  gint *Ldone = (gint *)sc_grave_get_data(Ag, "done"); /* fais */
  gint *Ltodo = (gint *) sc_grave_get_data(Ag, "todo");
  gint *Lpid = (gint *) sc_grave_get_data(Ag, "pid");
  
  gint g_out, g_err, Lnbrarg;
  gboolean Lstatus = FALSE;
  gchar *Lbufvitesse;
  Tdriveinfo *Ldevice = matos_get_drive_info(Ag, "dstaudiocombo");
  gboolean Lsimul = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sc_grave_get_widget(Ag, "audiosimul")));
  gboolean Lnotfix = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sc_grave_get_widget(Ag, "audionotfix")));
  gboolean Ldaomode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sc_grave_get_widget(Ag, "audiodao")));
  gint Lnbrgravure = gtk_spin_button_get_value(GTK_SPIN_BUTTON(sc_grave_get_widget(Ag, "nbrcdaudio")));
  gboolean Lwanteject = conf_get_boolean("eject");
  gint Lcurcd;
  gboolean Ldosimul;
  gint Lnbrpass=1;
  gchar *Lfileslist, *Lblank;
  gboolean Leject = FALSE;
  gchar Llasttrack[_BUF_SIZE] = "00";
  gint Lmediadetect1 = _MEDIA_CDRW;

  Lfileslist = make_audio_fileslist(*Ltodo);
  Lbufvitesse = get_combo_value(Lvitesse);

  *Ltodo = (*Ltodo) * ((Lnbrgravure) + (Lsimul ? 1 : 0));

  
  for (Lcurcd=1; Lcurcd<= Lnbrgravure;
      ((Lsimul && Lnbrpass>1) || (!Lsimul)) ? ( Lcurcd++, Lnbrpass=1 ) : ( Lnbrpass++ )) { 
    *Loperationstatus = FALSE;
    Ldosimul = (Lsimul && Lnbrpass==1);

    if (Lcurcd > 1 && !Ldosimul) {
      /* copie sur un nouveau cd, on demande a l'utilisateur d'inserer le
       * nouveau cd vierge */
      GSList *Llmediarequis1 = sc_grave_get_data(Ag, "mediarequis1");
      gint *Lmediatitle1 = sc_grave_get_data(Ag, "mediatitle1");
      gchar *Ltxt = sc_grave_get_data(Ag, "medialabel");
      gboolean Lstatus;
      Tgrave *Ldialoghash;
      GtkWidget *Lconfirm;
      gint Lrep;

      eject_cd(DRIVE_DEV(Ldevice), NULL);
      Lstatus = waiting_for_user(*Lmediatitle1, Ag, Llmediarequis1 , &Lmediadetect1, Ldevice);
          
      if (Lstatus==FALSE) {
        /* si c'est non alors on arrete */
        *Labort = TRUE; Lstatus = TRUE; break;
      } 
      
      Ldialoghash = create_dialog_confirm_operation(GTK_WIDGET(Lwindow), Ltxt,
       DRIVE_CDRW_WRITER(Ldevice) == TRUE &&
        ((Lmediadetect1 & _MEDIA_CDRW) && (Lmediadetect1 & _MEDIA_NOBLANK)) ?
        _("Blank CD-RW before writing") : NULL, conf_get_boolean("autoblank"));

      Lconfirm = sc_grave_get_data(Ldialoghash, "window");
      Lrep = gtk_dialog_run(GTK_DIALOG(Lconfirm));

      if (Lrep != GTK_RESPONSE_YES) {
        /* si c'est non alors on arrete */
        *Labort = TRUE; Lstatus = TRUE;
      } else if (DRIVE_CDRW_WRITER(Ldevice) == TRUE) {
        gboolean *Luseautoblank = (gboolean *) sc_grave_get_data(Ag, "useautoblank");
        GtkCheckButton *Labcheck = GTK_CHECK_BUTTON(sc_grave_get_widget(Ldialoghash, "checkbox"));
        *Luseautoblank = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(Labcheck));
      }
      gtk_widget_destroy(Lconfirm);

      if (*Labort == TRUE) break;
    }

    Lbuftitle = get_title(Lnbrgravure, Lcurcd, Ldosimul);
    gtk_label_set_text(GTK_LABEL(Ltitle), Lbuftitle);
    g_free(Lbuftitle);

    /* faut il ejecter le CD-R apres l'operation ?
     * oui si l'utilisateur a cocher la case "ejecter le cd" ou
     * si il faut realiser d'autre gravure sur d'autres CD-R */
    Leject = ((Lcurcd > 1 && (Lwanteject || Lcurcd<Lnbrgravure)) || (Lcurcd == 1 && !Ldosimul && Lwanteject));

    Lblank = get_blank_type(Ag, Lmediadetect1);
    Lcommandline = g_strdup_printf("%s dev=%s -v gracetime=2 %s%s %s%s %s %s %s %s %s -pad -audio %s %s",
        conf_get_string("cdrecord"), DRIVE_DEV(Ldevice),
        *Lbufvitesse == '0' ? "" : "speed=", *Lbufvitesse == '0' ? "" : Lbufvitesse,
        Ldosimul ? " -dummy" : "",  /* simulation ? */ 
        Ldosimul || Lnotfix ? " -nofix" : "", /* fixer le cd apres ecriture ? */
        Ldaomode ? " -dao" : "",  /* ecriture en mode DAO, pas de pause entre les pistes */
        Leject ? "-eject" : "", /* ejecter le cd apres l'operation */
        ((Lsimul && Lnbrpass != 2) || (!Lsimul)) ? Lblank : "", /* effacer le cdrw avant ?
                                                * on efface pas le cd apres la simulation vu que l'operation
                                                * a été faite juste avant */
        conf_get_string("cdrecordpara"),
        conf_get_boolean("overburn") ? "-overburn" : "",
        Lfileslist,
        DRIVE_BURN_FREE(Ldevice) ? "driveropts=burnfree" : ""
        );
    g_free(Lblank);
_DEB("execution [%s]\n", Lcommandline);
    Lstatus = g_shell_parse_argv(Lcommandline, &Lnbrarg, &Lcmd, Aerror);
    g_free(Lcommandline);
    if (Lstatus == FALSE) {
      break;
    }

    /* try to umount device before device access */
    matos_umount_device(Ldevice, NULL);

    Lstatus = g_spawn_async_with_pipes(Lrepertoire, Lcmd, NULL, /* env argument */
        (GSpawnFlags ) (G_SPAWN_DO_NOT_REAP_CHILD),
        NULL, NULL, Lpid, NULL, &g_out, &g_err, Aerror);
    g_strfreev(Lcmd);

    if (Lstatus == FALSE) {
      g_warning("ERROR EXECUTION !\n");
      break;
    }

    *Lcont = 1;
    sc_grave_set_data(Ag, &Llasttrack, "lasttrack");
    Lcom = g_io_channel_unix_new( g_out );
    g_io_channel_set_encoding (Lcom, NULL, NULL);
    g_io_channel_set_buffered(Lcom, FALSE);
    g_io_channel_set_flags( Lcom, G_IO_FLAG_NONBLOCK, NULL );
    Lcomevent = g_io_add_watch (Lcom, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                        cdrecord_grave_callback, Ag);
  
    Lcomerr = g_io_channel_unix_new( g_err );
    g_io_channel_set_encoding (Lcomerr, NULL, NULL);
    g_io_channel_set_buffered(Lcomerr, FALSE);
    g_io_channel_set_flags( Lcomerr, G_IO_FLAG_NONBLOCK, NULL );
    Lcomerrevent = g_io_add_watch (Lcomerr, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                        cdrecord_grave_callback, Ag);  
    
    while (*Lcont>0 && *Labort == FALSE) {
      gtk_main_iteration(); 
    }
    exit_prog(*Lpid, FALSE, Aerror, NULL);

    g_source_remove(Lcomerrevent);
    g_source_remove(Lcomevent);

    g_io_channel_shutdown(Lcomerr, FALSE, NULL);
    g_io_channel_unref(Lcomerr);  
    g_io_channel_shutdown(Lcom, FALSE, NULL);
    g_io_channel_unref(Lcom);
    g_spawn_close_pid(*Lpid);
    *Lpid = 0;

    sc_grave_del_data(Ag, "lasttrack");

    if (*Aerror) {
      Lstatus = FALSE;
      break;
    }
    if (*Loperationstatus == FALSE) {
      /* cdrecord n'a pas reussi a grave le cd mais on n'a intercepte aucune erreur ! argh!
       * dans tous les cas ce n'est pas normal, on genere une erreur */
      g_set_error(Aerror, GRAVEMAN_ERROR, _ERR_UNKNOWN_ERROR, 
          _("Communication error with cdrecord. Check that you have access to cdrecord release 2.0 !"));
      Lstatus = FALSE;
      break;
    }

    if (Ldosimul) {
      /* fin de la simulation, tout s'est apparement bien passe
       * on demande confirmation avent de commencer la vrai gravure */
      gint Lrep;
      GtkWidget *Lconfirm = gtk_message_dialog_new(Lwindow,
                                            GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
                                            GTK_BUTTONS_YES_NO, 
                      _("Simulation successful. Do you want to write the CD for real?"));
      Lrep = gtk_dialog_run(GTK_DIALOG(Lconfirm));
      gtk_widget_destroy(Lconfirm);
      if (Lrep == GTK_RESPONSE_NO) {
        /* si c'est non alors on arrete */
        *Labort = TRUE;
        Lstatus = TRUE;
        break;
      }
    }
    (*Ldone)++;
    strcpy(Llasttrack, "00");
  }

  g_free(Lbufvitesse);
  g_free(Lfileslist);

  return Lstatus;
}

gboolean common_blankorfix_callback(GIOChannel *Astd, GIOCondition Acond, gpointer Adata)
{
  GIOStatus Lstatus;
  Tgrave *Lg = (Tgrave *)Adata;
  gint *Lcont = (gint *) sc_grave_get_data(Lg, "cont"); /* on traite encore des donnees ? */
  gchar *Lbuffer = NULL;
  /* fin du callback lorsque l'on recoi un signal comme quoi le pipe est ferme */
  if (Acond == G_IO_HUP || Acond == G_IO_ERR) {
    (*Lcont) = 1;
    return FALSE;
  }

  Lstatus = g_io_channel_read_line(Astd, &Lbuffer, NULL, NULL, NULL);  
  if (!Lbuffer) return TRUE;

_DEB("===>%s", Lbuffer);

  if (!strcmp(Lbuffer, CDRECORD_BLANKING_TIME)) {
    /* fin de l'operation: effacer un cdrw */
    gboolean *Loperationstatus = (gboolean *)sc_grave_get_data(Lg, "operationstatus");
    *Loperationstatus = TRUE;
  } else if (!strcmp(Lbuffer, CDRECORD_FIXATING_TIME)) {
    /* fin de l'operation: cloturer un cdr */
     gboolean *Loperationstatus = (gboolean *)sc_grave_get_data(Lg, "operationstatus");
    *Loperationstatus = TRUE;
  } else if (strstr(Lbuffer, CDRECORD_CANNOT_BLANK)) {
    GError **Lerreur = (GError **) sc_grave_get_data(Lg, "gerror"); /* pointeur erreur */
    if (!*Lerreur)
      g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_CANNOT_BLANK, _("Cannot blank disk, aborting."));
  } else if (strstr(Lbuffer, CDRECORD_INCOMPATIBLE_MEDIUM)) {
    /* hum, erreur */
    GError **Lerreur = (GError **) sc_grave_get_data(Lg, "gerror"); /* pointeur erreur */
    if (!*Lerreur)
      g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_INCOMPATIBLE_MEDIUM, _("Cannot blank disk, this is not a CD-RW !"));
  } else if (strstr(Lbuffer, CDRECORD_ERRORDISK)) {
    /* erreur pas de cd */
    GError **Lerreur = (GError **) sc_grave_get_data(Lg, "gerror"); /* pointeur erreur */
    if (!*Lerreur)
      g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_NO_CD, _("Error, a CD-R/CD-RW is required in the cd recorder !"));
  }

  g_free(Lbuffer);
  return TRUE;
}

/* cloturer un cdr*/
gboolean perform_fix_cd(Tgrave *Ag, GError **Aerror)
{
  gchar **Lcmd;
  gchar *Lcommandline;
  gboolean Lstatus = FALSE;
  GIOChannel *Lcom, *Lcomerr;
  guint Lcomevent, Lcomerrevent;
  gint g_out, g_err, Lnbrarg;
  gint *Lcont = sc_grave_get_data(Ag, "cont");
  Tdriveinfo *Ldevice = matos_get_drive_info(Ag, "dstothercombo");

  /* pid de cdrecord */
  gint *Lpid = (gint *) sc_grave_get_data(Ag, "pid");

  Lcommandline = g_strdup_printf("%s dev=%s -v -fix", conf_get_string("cdrecord"), DRIVE_DEV(Ldevice));

  _DEB("execution [%s]\n", Lcommandline);
  Lstatus = g_shell_parse_argv(Lcommandline, &Lnbrarg, &Lcmd, Aerror);
  g_free(Lcommandline);

  if (Lstatus == FALSE) {
    return FALSE;
  }

  /* try to umount device before device access */
  matos_umount_device(Ldevice, NULL);

  Lstatus = g_spawn_async_with_pipes(NULL, Lcmd, NULL, /* env argument */
      (GSpawnFlags ) (G_SPAWN_DO_NOT_REAP_CHILD),
       NULL, NULL, Lpid, NULL, &g_out, &g_err, Aerror);
  g_strfreev(Lcmd);

  if (Lstatus == FALSE) {
    g_warning("ERROR EXECUTION !\n");
    return FALSE;
  }
  Lcom = g_io_channel_unix_new( g_out );
  g_io_channel_set_encoding (Lcom, NULL, NULL);
  g_io_channel_set_flags( Lcom, G_IO_FLAG_NONBLOCK, NULL );
  Lcomevent = g_io_add_watch (Lcom, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                      common_blankorfix_callback, Ag);
  
  Lcomerr = g_io_channel_unix_new( g_err );
  g_io_channel_set_encoding (Lcomerr, NULL, NULL);
  g_io_channel_set_flags( Lcomerr, G_IO_FLAG_NONBLOCK, NULL );
  Lcomerrevent = g_io_add_watch (Lcomerr, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                      common_blankorfix_callback, Ag);  
_DEB("cont vaut => %d\n", *Lcont);
  while (*Lcont > 1) {
    gtk_main_iteration(); 
  }
_DEB("fin !!!\n");
  while (*Lcont > 0) { while(gtk_events_pending()) gtk_main_iteration(); }

  exit_prog(*Lpid, FALSE, Aerror, NULL);

  g_source_remove(Lcomerrevent);
  g_source_remove(Lcomevent);

  g_io_channel_shutdown(Lcomerr, FALSE, NULL);
  g_io_channel_unref(Lcomerr);  
  g_io_channel_shutdown(Lcom, FALSE, NULL);
  g_io_channel_unref(Lcom);
  g_spawn_close_pid(*Lpid);
  *Lpid = 0;

  return *Aerror ? FALSE : TRUE;
}


/* ejecter un CD */
void eject_cd(gchar *Adev, GError **Aerror)
{
  gchar *Lcommandline = g_strdup_printf("%s -eject dev=%s", conf_get_string("cdrecord"), Adev);
  _DEB("EJECT %s\n", Adev);
  
  g_spawn_command_line_sync(Lcommandline, NULL, NULL, NULL, Aerror);

  g_free(Lcommandline);
}

/* effacer un cdrw */
gboolean perform_erase_cdrw(Tgrave *Ag, GError **Aerror)
{
  gchar **Lcmd;
  gchar *Lcommandline;
  gboolean Lstatus = FALSE;
  GIOChannel *Lcom, *Lcomerr;
  guint Lcomevent, Lcomerrevent;
  gint g_out, g_err, Lnbrarg;
  gint *Lcont = sc_grave_get_data(Ag, "cont");
  Tdriveinfo *Ldevice = matos_get_drive_info(Ag, "dstothercombo");
  gboolean *Lfastblank = sc_grave_get_data(Ag, "usefastblank");

  /* pid de cdrecord */
  gint *Lpid = (gint *) sc_grave_get_data(Ag, "pid");

  Lcommandline = g_strdup_printf("%s dev=%s -v gracetime=2 blank=%s",
      conf_get_string("cdrecord"), DRIVE_DEV(Ldevice), (*Lfastblank == TRUE ? "fast" : "all"));

  _DEB("execution [%s]\n", Lcommandline);
  Lstatus = g_shell_parse_argv(Lcommandline, &Lnbrarg, &Lcmd, Aerror);
  g_free(Lcommandline);

  if (Lstatus == FALSE) {
    return FALSE;
  }

  /* try to umount device before device access */
  matos_umount_device(Ldevice, NULL);

  Lstatus = g_spawn_async_with_pipes(NULL, Lcmd, NULL, /* env argument */
      (GSpawnFlags ) (G_SPAWN_DO_NOT_REAP_CHILD),
       NULL, NULL, Lpid, NULL, &g_out, &g_err, Aerror);
  g_strfreev(Lcmd);

  if (Lstatus == FALSE) {
    g_warning("ERROR EXECUTION !\n");
    return FALSE;
  }
  Lcom = g_io_channel_unix_new( g_out );
  g_io_channel_set_encoding (Lcom, NULL, NULL);
  g_io_channel_set_flags( Lcom, G_IO_FLAG_NONBLOCK, NULL );
  Lcomevent = g_io_add_watch (Lcom, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                      common_blankorfix_callback, Ag);
  
  Lcomerr = g_io_channel_unix_new( g_err );
  g_io_channel_set_encoding (Lcomerr, NULL, NULL);
  g_io_channel_set_flags( Lcomerr, G_IO_FLAG_NONBLOCK, NULL );
  Lcomerrevent = g_io_add_watch (Lcomerr, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                      common_blankorfix_callback, Ag);  
_DEB("cont vaut => %d\n", *Lcont);
  while (*Lcont > 1) {
    gtk_main_iteration(); 
  }
_DEB("fin !!!\n");
  while (*Lcont > 0) { while(gtk_events_pending()) gtk_main_iteration(); }

  exit_prog(*Lpid, FALSE, Aerror, NULL);

  g_source_remove(Lcomerrevent);
  g_source_remove(Lcomevent);

  g_io_channel_shutdown(Lcomerr, FALSE, NULL);
  g_io_channel_unref(Lcomerr);  
  g_io_channel_shutdown(Lcom, FALSE, NULL);
  g_io_channel_unref(Lcom);
  g_spawn_close_pid(*Lpid);
  *Lpid = 0;

  return *Aerror ? FALSE : TRUE;
}

gboolean cdrecord_mediainfo_callback(GIOChannel *Astd, GIOCondition Acond, gpointer Adata)
{
  GIOStatus Lstatus;
  Tgrave *Lg = (Tgrave *)Adata;
  gint *Lcont = (gint *) sc_grave_get_data(Lg, "cont"); /* on traite encore des donnees ? */
  gchar *Ltrouve = (gchar *) sc_grave_get_data(Lg, "msinfo");
  gchar *Lbuffer = NULL;
  gchar *s;
  gint Lniveau = 0;

  /* fin du callback lorsque l'on recoi un signal comme quoi le pipe est ferme */
  if (Acond == G_IO_HUP || Acond == G_IO_ERR) {
    *Lcont = 0;
    return FALSE;
  }
  Lstatus = g_io_channel_read_line(Astd, &Lbuffer, NULL, NULL, NULL);    

  for (s=Lbuffer; *s; s++) {
    if (isdigit(*s)) {
      if (Lniveau == 2) {
        Lniveau = 3;
      } else if (Lniveau == 0) {
        Lniveau = 1;
      }
    } else if (*s==',') {
      if (Lniveau == 1) {
        Lniveau = 2;
      } else {
        Lniveau = 0;
        break;
      }
    } else {
      if (Lniveau != 3) Lniveau = 0;
      break;
    }
  }

  if (Lniveau == 3) {
    g_strlcpy(Ltrouve, Lbuffer, _BUF_SIZE-1);
  }

  g_free(Lbuffer);
  
  return TRUE;
}

/* retourne les informations sur le disque courant pour par exemple
 * continuer une gravure multi session */
gchar *cdrecord_get_mediainfo(Tgrave *Ag, Tdriveinfo *Adevice, GError **Aerror)
{
  gchar **Lcmd;
  gchar *Lcommandline;
  gint *Lpid = (gint *) sc_grave_get_data(Ag, "pid");
  gint *Lcont = (gint *)sc_grave_get_data(Ag, "cont");
  gboolean *Labort = (gboolean *) sc_grave_get_data(Ag, "gabort");
  gboolean Lstatus = FALSE;
  gchar Lmsinfo[_BUF_SIZE] = "";
  GIOChannel *Lcom;
  guint Lcomevent;
  gint Lnbrarg, g_out, g_err;
  guint Ltimeout;

  (*Lcont) = 2;
  Ltimeout = g_timeout_add(500, encours_callback, Ag);

  sc_grave_set_data(Ag, &Lmsinfo, "msinfo");
  
  Lcommandline = g_strdup_printf("%s dev=%s -msinfo", conf_get_string("cdrecord"), DRIVE_DEV(Adevice));
_DEB("1execution [%s]", Lcommandline);
  Lstatus = g_shell_parse_argv(Lcommandline, &Lnbrarg, &Lcmd, Aerror);
  g_free(Lcommandline);
  if (Lstatus != FALSE) { 
    *Lcont = 2;

    Lstatus = g_spawn_async_with_pipes(NULL, Lcmd, NULL, (GSpawnFlags) (G_SPAWN_DO_NOT_REAP_CHILD),
        NULL, NULL, Lpid, NULL, &g_out, &g_err, Aerror);

    Lcom = g_io_channel_unix_new( g_out );
    g_io_channel_set_encoding (Lcom, NULL, NULL);
    g_io_channel_set_flags( Lcom, G_IO_FLAG_NONBLOCK, NULL );
    Lcomevent = g_io_add_watch (Lcom, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                      cdrecord_mediainfo_callback, Ag);

    while (*Lcont > 0 && *Labort == FALSE) {
     gtk_main_iteration();
    }
    exit_prog(*Lpid, FALSE, Aerror, NULL);

    g_source_remove(Lcomevent);
    g_io_channel_shutdown(Lcom, FALSE, NULL);
    g_io_channel_unref(Lcom);
    g_spawn_close_pid(*Lpid);
  }
  g_strfreev(Lcmd);

  sc_grave_del_data(Ag, "msinfo");

  /* attente de la fin du timeout */ 
  g_source_remove(Ltimeout);
  sc_chomp(Lmsinfo);
  _DEB("msinfo = [%s]\n", Lmsinfo);

  return *Lmsinfo ? g_strdup(Lmsinfo) : NULL;
}


/*
 * vim:et:ts=8:sts=2:sw=2
 */
