Logo Search packages:      
Sourcecode: nautilus-cd-burner version File versions

nautilus-burn-recorder.c

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
 *
 * nautilus-burn-recorder.c
 *
 * Copyright (C) 2002-2004 Bastien Nocera <hadess@hadess.net>
 * Copyright (C) 2005-2006 William Jon McCann <mccann@jhu.edu>
 *
 * This program 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 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * Authors: Bastien Nocera <hadess@hadess.net>
 *          William Jon McCann <mccann@jhu.edu>
 */

#include "config.h"

#include <locale.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <time.h>

#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>

#ifdef USE_HAL
#include <libhal.h>
#endif

#include "nautilus-burn-process.h"
#include "nautilus-burn-recorder.h"
#include "nautilus-burn-recorder-marshal.h"

#define CDR_SPEED 153600
#define DVD_SPEED 1385000

#define NAUTILUS_BURN_RECORDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NAUTILUS_BURN_TYPE_RECORDER, NautilusBurnRecorderPrivate))

struct NautilusBurnRecorderPrivate {
      NautilusBurnProcess *process;

      GList               *tracks;
      gint32               track_count;
      guint32              current_track_num;
      guint64              current_track_end_pos;
      guint64              tracks_total_bytes;
      gboolean             debug;
      gboolean             can_rewrite;

#ifdef USE_HAL
      LibHalContext *ctx;
#endif
};

/* Signals */
enum {
      PROGRESS_CHANGED,
      ACTION_CHANGED,
      ANIMATION_CHANGED,
      INSERT_MEDIA_REQUEST,
      WARN_DATA_LOSS,
      LAST_SIGNAL
};

static int nautilus_burn_recorder_table_signals [LAST_SIGNAL] = { 0 };

static int  nautilus_burn_recorder_write_cdrecord  (NautilusBurnRecorder          *recorder,
                                        NautilusBurnDrive             *drive,
                                        GList                         *tracks,
                                        int                            speed,
                                        NautilusBurnRecorderWriteFlags flags,
                                        GError                       **error);
static int  nautilus_burn_recorder_write_growisofs (NautilusBurnRecorder          *recorder,
                                        NautilusBurnDrive             *drive,
                                        GList                         *tracks,
                                        int                            speed,
                                        NautilusBurnRecorderWriteFlags flags,
                                        GError                       **error);

G_DEFINE_TYPE(NautilusBurnRecorder, nautilus_burn_recorder, G_TYPE_OBJECT);

GQuark
nautilus_burn_recorder_error_quark (void)
{
      static GQuark quark = 0;
      if (! quark)
            quark = g_quark_from_static_string ("nautilus_burn_recorder_error");

      return quark;
}

/**
 * nautilus_burn_recorder_track_free:
 * @track: #NautilusBurnRecorderTrack
 *
 * Free track data.
 *
 **/
void
nautilus_burn_recorder_track_free (NautilusBurnRecorderTrack *track)
{
      switch (track->type) {
      case NAUTILUS_BURN_RECORDER_TRACK_TYPE_CUE:
            g_free (track->contents.cue.filename);
            break;
      case NAUTILUS_BURN_RECORDER_TRACK_TYPE_DATA:
            g_free (track->contents.data.filename);
            break;
      case NAUTILUS_BURN_RECORDER_TRACK_TYPE_AUDIO:
            g_free (track->contents.audio.filename);
            g_free (track->contents.audio.cdtext);
            break;
      case NAUTILUS_BURN_RECORDER_TRACK_TYPE_GRAFT_LIST:
            g_strfreev (track->contents.graft_list.entries);
            g_free (track->contents.graft_list.label);
            break;
      default:
            g_warning ("Invalid track type %d", track->type);
            break;
      }

      g_free (track);
}

static gboolean
can_burn_dvds (NautilusBurnDrive *drive)
{
      int type;

      type = nautilus_burn_drive_get_drive_type (drive);

      if (!(type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER) &&
          !(type & NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER) &&
          !(type & NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_DL_RECORDER) &&
          !(type & NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER)) {
            return FALSE;
      }

      return TRUE;
}

static gboolean
cd_write_needs_growisofs (NautilusBurnDrive    *recorder,
                    NautilusBurnMediaType type,
                    GList                *tracks)
{
      GList *l;

      /* If we cannot burn DVDs, then we don't need growisofs */
      if (can_burn_dvds (recorder) == FALSE) {
            return FALSE;
      }

      if (type == NAUTILUS_BURN_MEDIA_TYPE_DVDR
          || type == NAUTILUS_BURN_MEDIA_TYPE_DVDRW
          || type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R
          || type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R_DL
          || type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW) {
            return TRUE;
      }

      /* If we have audio tracks, we're using cdrecord */
      for (l = tracks; l != NULL; l = l->next) {
            NautilusBurnRecorderTrack *track = l->data;
            if (track->type == NAUTILUS_BURN_RECORDER_TRACK_TYPE_AUDIO) {
                  return FALSE;
            }
      }

      if (((NautilusBurnRecorderTrack *)tracks->data)->type == NAUTILUS_BURN_RECORDER_TRACK_TYPE_GRAFT_LIST) {
            return TRUE;
      }

      return FALSE;
}

/**
 * nautilus_burn_recorder_cancel:
 * @recorder: #NautilusBurnRecorder
 * @skip_if_dangerous:
 *
 * Cancel active writing process.
 *
 * Return value: %TRUE if succesfully cancelled, %FALSE otherwise
 **/
gboolean
nautilus_burn_recorder_cancel (NautilusBurnRecorder *recorder,
                         gboolean              skip_if_dangerous)
{
      gboolean res;

      g_return_val_if_fail (recorder != NULL, FALSE);

      g_return_val_if_fail (recorder->priv->process != NULL, FALSE);

      res = nautilus_burn_process_cancel (recorder->priv->process,
                                  skip_if_dangerous);
      if (res) {
            recorder->priv->process->result = NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      return res;
}

static void
insert_cd_retry (NautilusBurnProcess *process,
             gboolean             cancel,
             gboolean             is_reload,
             gboolean             send_return)
{
      if (cancel) {
            /* Shouldn't be dangerous on reload */
            nautilus_burn_process_cancel (process, FALSE);
      } else if (is_reload) {
            if (send_return) {
                  write (process->pipe_stdin, "\n", 1);
            } else {
                  kill (process->pid, SIGUSR1);
            }
      } else {
            process->result = NAUTILUS_BURN_RECORDER_RESULT_RETRY;
            if (g_main_loop_is_running (process->loop))
                  g_main_loop_quit (process->loop);
      }
}

static long
compute_time_remaining (gint64 bytes,
                  double bytes_per_sec)
{
      long secs;

      if (bytes_per_sec <= 1) {
            return -1;
      }

      secs = bytes / bytes_per_sec;

      return secs;
}

static gboolean
growisofs_stdout_line (NautilusBurnProcess   *process,
                   const char            *line,
                   gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      int                   perc_1, perc_2;
      long long             b_written, b_total;
      float                 speed;

      if (line && process->debug) {
            g_print ("growisofs stdout: %s", line);
      }

      if (sscanf (line, "%10lld/%lld ( %2d.%1d%%) @%fx,", &b_written, &b_total, &perc_1, &perc_2, &speed) == 5) {
            double percent;
            long   secs;

            if (!process->changed_text) {
                  g_signal_emit (recorder,
                               nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                               NAUTILUS_BURN_RECORDER_ACTION_WRITING,
                               NAUTILUS_BURN_RECORDER_MEDIA_DVD);
            }

            percent = (perc_1 + ((float) perc_2 / 10)) / 100;
            secs = compute_time_remaining (b_total - b_written, (double)speed * DVD_SPEED);

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         percent, secs);
      } else if (strstr (line, "About to execute") != NULL) {
            process->dangerous = TRUE;
      }

      return TRUE;
}

static gboolean
growisofs_blank_stdout_line (NautilusBurnProcess   *process,
                       const char            *line,
                       gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      int                   perc_1, perc_2;
      long long             b_written, b_total;
      float                 speed;

      if (line && process->debug) {
            g_print ("growisofs blank stdout: %s", line);
      }

      if (sscanf (line, "%10lld/%lld ( %2d.%1d%%) @%fx,", &b_written, &b_total, &perc_1, &perc_2, &speed) == 5) {
            double percent;
            long   secs;

            if (!process->changed_text) {
                  g_signal_emit (recorder,
                               nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                               NAUTILUS_BURN_RECORDER_ACTION_BLANKING,
                               NAUTILUS_BURN_RECORDER_MEDIA_DVD);
            }

            percent = (perc_1 + ((float) perc_2 / 10)) / 100;
            secs = compute_time_remaining (b_total - b_written, (double)speed * DVD_SPEED);

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         percent, secs);
      } else if (strstr (line, "About to execute") != NULL) {
            process->dangerous = TRUE;
      }

      return TRUE;
}

static gdouble
get_average_rate (GList  **rates,
              gdouble  rate)
{
      const unsigned int max_num = 16;
      const unsigned int scale  = 1000;
      gdouble            average;
      gint32             int_rate;
      GList             *l;

      if (g_list_length (*rates) > max_num) {
            *rates = g_list_delete_link (*rates, *rates);
      }
      int_rate = (gint32)ceil (scale * rate);

      *rates = g_list_append (*rates, GINT_TO_POINTER (int_rate));

      average = 0;
      for (l = *rates; l != NULL; l = l->next) {
            gdouble r = (gdouble)GPOINTER_TO_INT (l->data) / scale;
            average += r;
      }

      average /= g_list_length (*rates);

      return average;
}

static gboolean
cdrecord_stdout_line (NautilusBurnProcess   *process,
                  const char            *line,
                  gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      unsigned int          track, mb_written, mb_total;
      float                 speed;
      int                   tmp;

      if (line && process->debug) {
            g_print ("cdrecord stdout: %s", line);
      }

      if (sscanf (line, "Track %2u: %d of %d MB written (fifo %d%%) [buf %d%%] %fx.",
                &track, &mb_written, &mb_total, &tmp, &tmp, &speed) == 6) {
            double       percent;
            gint64       bytes;
            gint64       this_remain;
            gint64       total;
            long         secs;

            if (recorder->priv->tracks_total_bytes > 0) {
                  total = recorder->priv->tracks_total_bytes;
            } else {
                  total = mb_total * 1048576;
            }

            if (track > recorder->priv->current_track_num) {
                  recorder->priv->current_track_num = track;
                  recorder->priv->current_track_end_pos += mb_total * 1048576;
            }

            this_remain = (mb_total - mb_written) * 1048576;
            bytes = (total - recorder->priv->current_track_end_pos) + this_remain;

            secs = -1;
            if (speed > 0) {
                  gdouble ave_rate;
                  gdouble rate;

                  rate = (double)speed * CDR_SPEED;

                  ave_rate = get_average_rate (&process->rates, rate);
                  secs = compute_time_remaining (bytes, ave_rate);
            }

            if (!process->changed_text) {
                  g_signal_emit (recorder,
                               nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                               NAUTILUS_BURN_RECORDER_ACTION_WRITING,
                               NAUTILUS_BURN_RECORDER_MEDIA_CD);
            }

            if (recorder->priv->tracks_total_bytes > 0) {
                  percent = 0.98 * (1.0 - (double)bytes / (double)recorder->priv->tracks_total_bytes);
            } else {
                  percent = 0.98 * ((double)((track - 1) / (double)recorder->priv->track_count)
                                + ((double)mb_written / mb_total) / (double)recorder->priv->track_count);
            }

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         percent, secs);
      } else if (sscanf (line, "Track %*d: %*s %d MB ", &mb_total)) {
            /* %s can be either "data" or "audio" */
            if (mb_total > 0) {
                  recorder->priv->tracks_total_bytes += mb_total * 1048576;
            }
      } else if (g_str_has_prefix (line, "Re-load disk and hit <CR>") ||
               g_str_has_prefix (line, "send SIGUSR1 to continue")) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               before starting, but we try to handle it anyway, since mmc
               profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, recorder->priv->can_rewrite,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, TRUE, (*line == 'R'));

      } else if (g_str_has_prefix (line, "Fixating...")) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_FIXATING,
                         NAUTILUS_BURN_RECORDER_MEDIA_CD);
      } else if (g_str_has_prefix (line, "Fixating time:")) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         1.0, (long)-1);
            /* only set result if not already set */
            if (process->result == 0) {
                  process->result = NAUTILUS_BURN_RECORDER_RESULT_FINISHED;
            }
      } else if (g_str_has_prefix (line, "Last chance to quit, ")) {
            process->dangerous = TRUE;
      } else if (g_str_has_prefix (line, "Blanking PMA, TOC, pregap")) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_BLANKING,
                         NAUTILUS_BURN_RECORDER_MEDIA_CD);
      }

      return TRUE;
}

static gboolean
cdrecord_blank_stdout_line (NautilusBurnProcess   *process,
                      const char            *line,
                      gpointer               data)
{
      NautilusBurnRecorder *recorder = data;

      if (line && process->debug) {
            g_print ("cdrecord blank stdout: %s", line);
      }

      if (g_str_has_prefix (line, "Re-load disk and hit <CR>") ||
          g_str_has_prefix (line, "send SIGUSR1 to continue")) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               before starting, but we try to handle it anyway, since mmc
               profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, recorder->priv->can_rewrite,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, TRUE, (*line == 'R'));
      } else if (g_str_has_prefix (line, "Blanking time:")) {
            if (process->result == 0) {
                  process->result = NAUTILUS_BURN_RECORDER_RESULT_FINISHED;
            }
      } else if (g_str_has_prefix (line, "Last chance to quit, ")) {
            process->dangerous = TRUE;
      } else if (g_str_has_prefix (line, "Blanking PMA, TOC, pregap")) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_BLANKING,
                         NAUTILUS_BURN_RECORDER_MEDIA_CD);
      }

      return TRUE;
}

static gboolean
dvdrw_format_stderr_line (NautilusBurnProcess   *process,
                    const char            *line,
                    gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      float                 percent;

      if (line && process->debug) {
            g_print ("dvdrw format stderr: %s", line);
      }

      if ((sscanf (line, "* blanking %f%%,", &percent) == 1)
          || (sscanf (line, "* formatting %f%%,", &percent) == 1)
          || (sscanf (line, "* relocating lead-out %f%%,", &percent) == 1)) {

            process->dangerous = TRUE;

            if (percent > 1) {
                  if (!process->changed_text) {
                        g_signal_emit (recorder,
                                     nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                                     NAUTILUS_BURN_RECORDER_ACTION_BLANKING,
                                     NAUTILUS_BURN_RECORDER_MEDIA_DVD);
                  }

                  g_signal_emit (recorder,
                               nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                               percent, (long)-1);
            }

      } else {

      }

      return TRUE;
}

static gboolean
growisofs_stderr_line (NautilusBurnProcess   *process,
                   const char            *line,
                   gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      char                 *pos;

      if (line && process->debug) {
            g_print ("growisofs stderr: %s", line);
      }

      if (strstr (line, "unsupported MMC profile") != NULL
          || (strstr (line, "already carries isofs") != NULL
            && strstr (line, "FATAL:") != NULL)) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               type before starting, but we try to handle it anyway, since mmc
               profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, recorder->priv->can_rewrite,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, FALSE, FALSE);
      } else if (strstr (line, "pre-formatting") != NULL) {
            process->dangerous = TRUE;

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_BLANKING,
                         NAUTILUS_BURN_RECORDER_MEDIA_DVD);
      } else if (strstr (line, "Current Write Speed") != NULL) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_WRITING,
                         NAUTILUS_BURN_RECORDER_MEDIA_DVD);
      } else if (strstr (line, "unable to open") != NULL || strstr (line, "unable to stat") != NULL) {
            /* This fits the "open64" and "open"-like messages */
            nautilus_burn_process_set_error (process,
                                     _("The recorder could not be accessed."),
                                     NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      } else if (strstr (line, "not enough space available") != NULL) {
            nautilus_burn_process_set_error (process,
                                     _("Not enough space available on the disc."),
                                     NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      } else if (strstr (line, "end of user area encountered on this track") != NULL) {
            nautilus_burn_process_set_error (process,
                                     _("The files selected did not fit on the DVD."),
                                     NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      } else if (strstr (line, "blocks are free") != NULL) {
            nautilus_burn_process_set_error (process,
                                     _("The files selected did not fit on the DVD."),
                                     NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      } else if (strstr (line, "flushing cache") != NULL) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_FIXATING,
                         NAUTILUS_BURN_RECORDER_MEDIA_DVD);
            if (process->result == 0) {
                  process->result = NAUTILUS_BURN_RECORDER_RESULT_FINISHED;
            }
      } else if (strstr (line, "unable to unmount") != NULL) {
            nautilus_burn_process_set_error (process,
                                     _("The target DVD drive is still in use."),
                                     NAUTILUS_BURN_RECORDER_RESULT_ERROR);

      } else if (strstr (line, ":-(") != NULL || strstr (line, "FATAL") != NULL) {
            if (! nautilus_burn_process_get_error (process, NULL, NULL)) {
                  nautilus_burn_process_set_error (process,
                                           _("Unhandled error, aborting"),
                                           NAUTILUS_BURN_RECORDER_RESULT_ERROR);
            }
      }

      /* for mkisofs exec */
      if ((pos = strstr (line, "estimate finish"))) {
            char    fraction_str [7];
            gdouble fraction;

            if (sscanf (line, "%6c%% done, estimate finish", fraction_str) == 1) {
                  long secs;

                  fraction_str [6] = 0;
                  fraction = g_strtod (fraction_str, NULL);

                  secs = -1;
                  if (pos) {
                        struct tm tm;
                        time_t    t_finish;

                        pos += strlen ("estimate finish ");
                        /* mkisofs uses ctime for format time string */
                        strptime (pos, "%a %b %d %H:%M:%S %Y", &tm);
                        t_finish = mktime (&tm);
                        secs = difftime (t_finish, time (NULL));
                  }

                  g_signal_emit (recorder,
                               nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                               fraction / 100.0, secs);
            }
      }

      return TRUE;
}

static gboolean
cdrecord_stderr_line (NautilusBurnProcess   *process,
                  const char            *line,
                  gpointer               data)
{
      NautilusBurnRecorder *recorder = data;

      if (line && process->debug) {
            g_print ("cdrecord stderr: %s", line);
      }

      if (strstr (line, "No disk / Wrong disk!") != NULL) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               before starting, but we try to handle it anyway, since mmc
               profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, recorder->priv->can_rewrite,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, FALSE, FALSE);
      } else if (strstr (line, "This means that we are checking recorded media.") != NULL) {
            nautilus_burn_process_set_error (process,
                                     _("The CD has already been recorded."),
                                     NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      } else if (strstr (line, "Cannot blank disk, aborting.") != NULL) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               type before starting, but we try to handle it anyway, since
               mmc profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, TRUE,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, FALSE, FALSE);
      } else if ((strstr (line, "Data may not fit on current disk") != NULL)
               || (strstr (line, "Data may not fit on standard") != NULL)) {
            nautilus_burn_process_set_error (process,
                                     _("The files selected did not fit on the CD."),
                                     NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      } else if (strstr (line, "Inappropriate audio coding") != NULL) {
            nautilus_burn_process_set_error (process,
                                     _("All audio files must be stereo, 16-bit digital audio with 44100Hz samples."),
                                     NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      } else if (strstr (line, "cannot write medium - incompatible format") != NULL) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               type before starting, but we try to handle it anyway, since
               mmc profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, recorder->priv->can_rewrite,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, FALSE, FALSE);
      } else if (strstr (line, "Sense flags: ") != NULL) {
            /*
              closest thing to an error message that we get:
              Sense flags: Blk 65469 (not valid)
            */
            if (strstr (line, "(not valid)") != NULL) {
                  nautilus_burn_process_set_error (process,
                                           _("Error while writing to disc.  Try a lower speed."),
                                           NAUTILUS_BURN_RECORDER_RESULT_ERROR);
            }
      } else if (strstr (line, "DMA speed too slow") != NULL) {
            nautilus_burn_process_set_error (process,
                                     _("The system is too slow to write the CD at this speed. Try a lower speed."),
                                     NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      }

      return TRUE;
}

static NautilusBurnMediaType
nautilus_burn_recorder_wait_for_insertion (NautilusBurnRecorder *recorder,
                                 NautilusBurnDrive    *drive,
                                 gboolean             *can_rewrite,
                                 gboolean             *blank)
{
      NautilusBurnMediaType type;
      gint64                media_capacity;
      gboolean              reload;
      gboolean              is_rewritable;
      gboolean              can_write;
      gboolean              is_blank;
      gboolean              has_data;
      gboolean              has_audio;

      reload = FALSE;

 again:

      /* if drive is mounted then unmount before checking anything */
      if (nautilus_burn_drive_is_mounted (drive)) {
            gboolean res;
            res = nautilus_burn_drive_unmount (drive);
            if (! res) {
                  g_warning ("Couldn't unmount volume in drive: %s", nautilus_burn_drive_get_device (drive));
            }
      }

      media_capacity = nautilus_burn_drive_get_media_capacity (drive);
      type = nautilus_burn_drive_get_media_type_full (drive, &is_rewritable, &is_blank, &has_data, &has_audio);
      can_write = nautilus_burn_drive_media_type_is_writable (type, is_blank);

      if (can_rewrite) {
            *can_rewrite = is_rewritable;
      }

      if (blank) {
            *blank = is_blank;
      }

      if (! can_write) {
            gboolean is_mounted;
            int      res;

            reload = (media_capacity > 0);
            if (type == NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
                  reload = FALSE;
            }

            /* check one more time */
            is_mounted = nautilus_burn_drive_is_mounted (drive);

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0,
                         reload,
                         recorder->priv->can_rewrite,
                         is_mounted,
                         &res);

            if (res == FALSE) {
                  return NAUTILUS_BURN_MEDIA_TYPE_ERROR;
            }

            goto again;
      }

      if (! is_blank) {
            int res;

            res = 0;

            /* We need to warn the user that it's going to erase their stuff */
            g_signal_emit (G_OBJECT (recorder),
                         nautilus_burn_recorder_table_signals [WARN_DATA_LOSS],
                         0, &res);

            if (res == NAUTILUS_BURN_RECORDER_RESPONSE_RETRY) {
                  goto again;
            } else if (res == NAUTILUS_BURN_RECORDER_RESPONSE_CANCEL) {
                  type = NAUTILUS_BURN_MEDIA_TYPE_ERROR;
            }
      }

      return type;
}

/**
 * nautilus_burn_recorder_write_tracks:
 * @recorder: #NautilusBurnRecorder
 * @drive: #NautilusBurnDrive to write to
 * @tracks: list of tracks to write
 * @speed: speed at which to write data
 * @flags: #NautilusBurnRecorderWriteFlags
 * @error: return location for errors
 *
 * Write tracks to the specified drive.
 *
 * Return value: #NautilusBurnRecorderResult
 **/
int
nautilus_burn_recorder_write_tracks (NautilusBurnRecorder          *recorder,
                             NautilusBurnDrive             *drive,
                             GList                         *tracks,
                             int                            speed,
                             NautilusBurnRecorderWriteFlags flags,
                             GError                       **error)
{
      NautilusBurnMediaType type;
      gboolean              can_rewrite;
      gboolean              is_blank;
      gboolean              is_locked;
      int                   ret;

      g_return_val_if_fail (recorder != NULL, NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      g_return_val_if_fail (tracks != NULL, NAUTILUS_BURN_RECORDER_RESULT_ERROR);

      recorder->priv->tracks = tracks;
      recorder->priv->track_count = g_list_length (tracks);
      recorder->priv->debug = (flags & NAUTILUS_BURN_RECORDER_WRITE_DEBUG);

      recorder->priv->can_rewrite = nautilus_burn_drive_can_rewrite (drive);

      if (recorder->priv->track_count > 99) {
            g_set_error (error,
                       NAUTILUS_BURN_RECORDER_ERROR,
                       NAUTILUS_BURN_RECORDER_ERROR_GENERAL,
                       _("You can only burn 99 tracks on one disc"));

            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      } else if (recorder->priv->track_count < 1) {
            g_set_error (error,
                       NAUTILUS_BURN_RECORDER_ERROR,
                       NAUTILUS_BURN_RECORDER_ERROR_GENERAL,
                       _("No tracks given to write"));

            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      }

      is_locked = nautilus_burn_drive_lock (drive, _("Burning CD/DVD"), NULL);

      type = nautilus_burn_recorder_wait_for_insertion (recorder,
                                            drive,
                                            &can_rewrite,
                                            &is_blank);

      if (type == NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
            if (is_locked)
                  nautilus_burn_drive_unlock (drive);

            return NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      if (can_rewrite != FALSE) {
            flags |= NAUTILUS_BURN_RECORDER_WRITE_BLANK;
      }

      if (cd_write_needs_growisofs (drive, type, tracks)) {
            ret = nautilus_burn_recorder_write_growisofs (recorder,
                                                drive,
                                                tracks,
                                                speed,
                                                flags,
                                                error);
      } else {
            ret = nautilus_burn_recorder_write_cdrecord (recorder,
                                               drive,
                                               tracks,
                                               speed,
                                               flags,
                                               error);
      }

      if (is_locked) {
            nautilus_burn_drive_unlock (drive);
      }

      return ret;
}

static int
nautilus_burn_recorder_run_process (NautilusBurnRecorder       *recorder,
                            NautilusBurnRecorderMedia   media_type,
                            GPtrArray                  *argv,
                            NautilusBurnProcessLineFunc out_line_func,
                            NautilusBurnProcessLineFunc err_line_func,
                            GError                    **error)
{
      int                  res;
      int                  result;
      GError              *local_error = NULL;
      NautilusBurnProcess *process;
      char                *error_message;
      int                  error_code;

 retry:

      process = nautilus_burn_process_new ();
      process->debug = recorder->priv->debug;

      nautilus_burn_process_free (recorder->priv->process);
      recorder->priv->process = process;

      process->result = 0;

      g_signal_emit (G_OBJECT (recorder),
                   nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                   NAUTILUS_BURN_RECORDER_ACTION_PREPARING_WRITE,
                   media_type);
      g_signal_emit (G_OBJECT (recorder),
                   nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                   0.0, (long)-1);
      g_signal_emit (G_OBJECT (recorder),
                   nautilus_burn_recorder_table_signals [ANIMATION_CHANGED], 0,
                   TRUE);

      local_error = NULL;
      res = nautilus_burn_process_run (process,
                               argv,
                               out_line_func,
                               err_line_func,
                               recorder,
                               &local_error);

      if (process->result == NAUTILUS_BURN_RECORDER_RESULT_RETRY) {
            goto retry;
      }

      if (local_error != NULL) {
            g_set_error (error,
                       NAUTILUS_BURN_RECORDER_ERROR,
                       NAUTILUS_BURN_RECORDER_ERROR_GENERAL,
                       local_error->message);
            g_error_free (local_error);
      } else if (nautilus_burn_process_get_error (process, &error_message, &error_code)) {
            g_set_error (error,
                       NAUTILUS_BURN_RECORDER_ERROR,
                       NAUTILUS_BURN_RECORDER_ERROR_GENERAL,
                       error_message);
            g_free (error_message);
      }

      result = process->result;
      nautilus_burn_process_free (process);
      recorder->priv->process = NULL;

      g_signal_emit (G_OBJECT (recorder),
                   nautilus_burn_recorder_table_signals [ANIMATION_CHANGED], 0,
                   FALSE);

      if (result == 0) {
            result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      }

      return result;
}

static char *
create_temp_file (void)
{
      char *template;
      char *template_path;
      int   fd;

      template = g_strdup_printf ("iso-%s.XXXXXX", g_get_user_name ());
      template_path = g_build_filename (g_get_tmp_dir (), template, NULL);
      g_free (template);

      fd = g_mkstemp (template_path);

      if (fd == -1) {
            g_free (template_path);
            template_path = NULL;
      } else {
            close (fd);
      }

      return template_path;
}

static char *
create_temp_graft_file (char **entries)
{
      char    *filename;
      char    *contents;
      gboolean res;

      filename = create_temp_file ();

      contents = g_strjoinv ("\n", entries);

      res = g_file_set_contents (filename, contents, -1, NULL);
      if (! res) {
            g_free (filename);
            filename = NULL;
      }

      g_free (contents);

      return filename;
}

/* this is an ugly hack until utf8/iconv support is added into upstream mkisofs */
static gboolean
ncb_mkisofs_supports_utf8 (void)
{
      static gboolean first = TRUE;
      static gboolean supported;

      if (first) {
            char    *standard_error;
            gboolean res;
            res = g_spawn_command_line_sync ("mkisofs -input-charset utf8", NULL, &standard_error, NULL, NULL);
            if (res && !g_strrstr (standard_error, "Unknown charset")) {
                  supported = TRUE;
            } else {
                  supported = FALSE;
            }
            g_free (standard_error);
      }

      return supported;
}

static int
nautilus_burn_recorder_write_growisofs (NautilusBurnRecorder          *recorder,
                              NautilusBurnDrive             *drive,
                              GList                         *tracks,
                              int                            speed,
                              NautilusBurnRecorderWriteFlags flags,
                              GError                       **error)
{
      GPtrArray                 *argv;
      char                      *speed_str = NULL;
      char                *dev_str;
      NautilusBurnRecorderTrack *t;
      int                        result;
      char                      *filelist;

      if (g_list_length (tracks) != 1) {
            g_warning ("Can only use growisofs on a single track");
            g_set_error (error,
                       NAUTILUS_BURN_RECORDER_ERROR,
                       NAUTILUS_BURN_RECORDER_ERROR_GENERAL,
                       _("Can only use growisofs on a single track"));

            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      }

      t = (NautilusBurnRecorderTrack*)tracks->data;
      if (t->type != NAUTILUS_BURN_RECORDER_TRACK_TYPE_DATA
          && t->type != NAUTILUS_BURN_RECORDER_TRACK_TYPE_GRAFT_LIST) {
            g_set_error (error,
                       NAUTILUS_BURN_RECORDER_ERROR,
                       NAUTILUS_BURN_RECORDER_ERROR_GENERAL,
                       _("Can only use growisofs on a data or graft list track"));
            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      }

      argv = g_ptr_array_new ();

      g_ptr_array_add (argv, "growisofs");
      if (speed != 0) {
            char  *current_locale = NULL;
            float  dvd_speed;

            dvd_speed = NAUTILUS_BURN_DRIVE_DVD_SPEED (speed);

            /* We want g_strdup_printf() to use dots as decimal
             * point for the speed, otherwise growisofs wouldn't understand. */
            current_locale = setlocale (LC_NUMERIC, NULL);
            setlocale (LC_NUMERIC, "C");

            speed_str = g_strdup_printf ("-speed=%.1f", dvd_speed);

            setlocale (LC_NUMERIC, current_locale);

            g_ptr_array_add (argv, speed_str);
      }

      g_ptr_array_add (argv, "-dvd-compat");

      /* Weird, innit? We tell growisofs we have a tty so it ignores
       * the fact that the DVD+ has an ISO fs already */
      if (flags & NAUTILUS_BURN_RECORDER_WRITE_BLANK) {
            g_ptr_array_add (argv, "-use-the-force-luke=tty");
      }

      filelist = NULL;
      if (t->type == NAUTILUS_BURN_RECORDER_TRACK_TYPE_GRAFT_LIST) {
            filelist = create_temp_graft_file (t->contents.graft_list.entries);

            g_ptr_array_add (argv, "-graft-points");
            g_ptr_array_add (argv, "-path-list");
            g_ptr_array_add (argv, filelist);
      }

      g_ptr_array_add (argv, "-Z");

      dev_str = NULL;
      if (t->type == NAUTILUS_BURN_RECORDER_TRACK_TYPE_DATA) {
            dev_str = g_strdup_printf ("%s=%s", nautilus_burn_drive_get_device (drive), t->contents.data.filename);
            g_ptr_array_add (argv, dev_str);
      } else {
            g_ptr_array_add (argv, (char *)nautilus_burn_drive_get_device (drive));

            /* mkisofs options */
            if (ncb_mkisofs_supports_utf8 ()) {
                  g_ptr_array_add (argv, "-input-charset");
                  g_ptr_array_add (argv, "utf8");
            }

            g_ptr_array_add (argv, "-r");

            if (flags & NAUTILUS_BURN_RECORDER_WRITE_JOLIET) {
                  g_ptr_array_add (argv, "-J");
            }

            g_ptr_array_add (argv, "-V");
            g_ptr_array_add (argv, (char *) t->contents.graft_list.label);
      }

      g_ptr_array_add (argv, NULL);

      result = nautilus_burn_recorder_run_process (recorder,
                                         NAUTILUS_BURN_RECORDER_MEDIA_DVD,
                                         argv,
                                         growisofs_stdout_line,
                                         growisofs_stderr_line,
                                         error);

      g_free (speed_str);
      g_free (dev_str);
      g_ptr_array_free (argv, TRUE);
      argv = NULL;

      if (filelist != NULL) {
            g_remove (filelist);
            g_free (filelist);
      }

      g_signal_emit (recorder,
                   nautilus_burn_recorder_table_signals [ANIMATION_CHANGED], 0,
                   FALSE);

      if (flags & NAUTILUS_BURN_RECORDER_WRITE_EJECT && result == NAUTILUS_BURN_RECORDER_RESULT_FINISHED) {
            nautilus_burn_drive_eject (drive);
      }

      return result;
}

static gboolean
cdrdao_stderr_line (NautilusBurnProcess   *process,
                const char            *line,
                gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      unsigned int          written, total;

      if (line && process->debug) {
            g_print ("cdrdao stderr: %s", line);
      }

      if (sscanf (line, "Wrote %u of %u", &written, &total) == 2) {
            long    secs = -1;
            gdouble fraction;

            process->dangerous = TRUE;

            fraction = (gdouble) written / total;

            if (process->start_time == 0
                && written > 2) {
                  /* let the speed regulate before keeping track */
                  process->start_time = time (NULL);
                  process->start_num = (gint64)written;
            }

            if (process->start_time > 0) {
                  guint64 elapsed;
                  gdouble rate;

                  elapsed = time (NULL) - process->start_time;
                  rate = (gdouble)(written - process->start_num) / (gdouble)elapsed;

                  if (rate > 0) {
                        secs = (total - written) / rate;
                  }
            }

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_WRITING,
                         NAUTILUS_BURN_RECORDER_MEDIA_CD);
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         fraction, secs);
      }

      if (strstr (line, "Writing track 01")) {
            process->dangerous = TRUE;
            process->start_time = 0;

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_WRITING,
                         NAUTILUS_BURN_RECORDER_MEDIA_CD);
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         0.0, (long)-1);

      }

      if (strstr (line, "Operation not permitted")) {
            nautilus_burn_process_set_error (process,
                                     line,
                                     NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      }

      if (strstr (line, "Writing finished successfully")) {
            process->dangerous = FALSE;
            process->result = NAUTILUS_BURN_RECORDER_RESULT_FINISHED;
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         1.0, (long)-1);

      }

      return TRUE;
}

static int
nautilus_burn_recorder_write_cdrecord (NautilusBurnRecorder          *recorder,
                               NautilusBurnDrive             *drive,
                               GList                         *tracks,
                               int                            speed,
                               NautilusBurnRecorderWriteFlags flags,
                               GError                       **error)
{
      NautilusBurnRecorderTrack  *track;
      GPtrArray                  *argv      = NULL;
      char                       *speed_str = NULL;
      char                       *dev_str   = NULL;
      char                       *cue_str   = NULL;
      GList                      *l;
      NautilusBurnProcessLineFunc out_line_func;
      NautilusBurnProcessLineFunc err_line_func;
      int                         result    = NAUTILUS_BURN_RECORDER_RESULT_ERROR;

      g_return_val_if_fail (tracks != NULL, NAUTILUS_BURN_RECORDER_RESULT_ERROR);

      track = tracks->data;

      if (flags & NAUTILUS_BURN_RECORDER_WRITE_BLANK) {
            NautilusBurnRecorderBlankFlags blank_flags = 0;
            int                            res;
            GError                        *err = NULL;

            if (flags & NAUTILUS_BURN_RECORDER_WRITE_DEBUG) {
                  blank_flags |= NAUTILUS_BURN_RECORDER_BLANK_DEBUG;
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_DUMMY_WRITE) {
                  blank_flags |= NAUTILUS_BURN_RECORDER_BLANK_DUMMY_WRITE;
            }

            /* cdrdao doesn't seem to have on-the-fly blank */
            /* cdrecord doesn't seem to correctly detect disc size when blanking */
            res = nautilus_burn_recorder_blank_disc (recorder,
                                           drive,
                                           NAUTILUS_BURN_RECORDER_BLANK_FAST,
                                           blank_flags,
                                           &err);
            if (err != NULL) {
                  g_propagate_error (error, err);
            }

            if (res != NAUTILUS_BURN_RECORDER_RESULT_FINISHED) {
                  return res;
            }
      }

      /* Turn the kB/s to a CD speed (for example 10 for "10x").
       * cdrecord supports only integer numbers for the speed. */
       speed = (int) NAUTILUS_BURN_DRIVE_CD_SPEED (speed);

      if (track->type ==  NAUTILUS_BURN_RECORDER_TRACK_TYPE_CUE) {
            argv = g_ptr_array_new ();
            g_ptr_array_add (argv, "cdrdao");
            g_ptr_array_add (argv, "write");
            g_ptr_array_add (argv, "--device");
            g_ptr_array_add (argv, (char *)nautilus_burn_drive_get_device (drive));
            g_ptr_array_add (argv, "--speed");
            speed_str = g_strdup_printf ("%d", speed);
            g_ptr_array_add (argv, speed_str);

            if (flags & NAUTILUS_BURN_RECORDER_WRITE_DUMMY_WRITE) {
                  g_ptr_array_add (argv, "--simulate");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_EJECT) {
                  g_ptr_array_add (argv, "--eject");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_OVERBURN) {
                  g_ptr_array_add (argv, "--overburn");
            }
            g_ptr_array_add (argv, "-v");
            g_ptr_array_add (argv, "2");

            g_ptr_array_add (argv, track->contents.cue.filename);
            g_ptr_array_add (argv, NULL);

            out_line_func = NULL;
            err_line_func = cdrdao_stderr_line;
      } else {
            argv = g_ptr_array_new ();
            g_ptr_array_add (argv, "cdrecord");

            g_ptr_array_add (argv, "fs=16m");

            speed_str = g_strdup_printf ("speed=%d", speed);
            if (speed != 0) {
                  g_ptr_array_add (argv, speed_str);
            }
            dev_str = g_strdup_printf ("dev=%s", nautilus_burn_drive_get_device (drive));
            g_ptr_array_add (argv, dev_str);
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_DUMMY_WRITE) {
                  g_ptr_array_add (argv, "-dummy");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_EJECT) {
                  g_ptr_array_add (argv, "-eject");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_DISC_AT_ONCE) {
                  g_ptr_array_add (argv, "-dao");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_OVERBURN) {
                  g_ptr_array_add (argv, "-overburn");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_BURNPROOF) {
                  g_ptr_array_add (argv, "driveropts=burnfree");
            }
            g_ptr_array_add (argv, "-v");

            l = tracks;
            while (l != NULL && l->data != NULL) {

                  NautilusBurnRecorderTrack *track = l->data;

                  switch (track->type) {
                  case NAUTILUS_BURN_RECORDER_TRACK_TYPE_DATA:
                        g_ptr_array_add (argv, "-data");
                        g_ptr_array_add (argv, "-nopad");
                        g_ptr_array_add (argv, track->contents.data.filename);
                        break;
                  case NAUTILUS_BURN_RECORDER_TRACK_TYPE_AUDIO:
                        g_ptr_array_add (argv, "-copy");
                        g_ptr_array_add (argv, "-audio");
                        g_ptr_array_add (argv, "-pad");
                        g_ptr_array_add (argv, track->contents.audio.filename);
                        /* TODO: handle CD-TEXT somehow */
                        break;
                  case NAUTILUS_BURN_RECORDER_TRACK_TYPE_CUE:
                        /* skip silently if not the first track */
                        break;
                  default:
                        g_warning ("Unknown track type %d", track->type);
                  }

                  l = g_list_next (l);
            }

            g_ptr_array_add (argv, NULL);

            out_line_func = cdrecord_stdout_line;
            err_line_func = cdrecord_stderr_line;
      }

      if (argv != NULL) {
            GError *err = NULL;

            result = nautilus_burn_recorder_run_process (recorder,
                                               NAUTILUS_BURN_RECORDER_MEDIA_CD,
                                               argv,
                                               out_line_func,
                                               err_line_func,
                                               &err);
            if (err != NULL) {
                  g_propagate_error (error, err);
            }
      }

      g_free (cue_str);
      g_free (speed_str);
      g_free (dev_str);
      g_ptr_array_free (argv, TRUE);
      argv = NULL;

      return result;
}

static int
nautilus_burn_recorder_blank_disc_cdrecord (NautilusBurnRecorder          *recorder,
                                  NautilusBurnDrive             *drive,
                                  NautilusBurnRecorderBlankType  type,
                                  NautilusBurnRecorderBlankFlags flags,
                                  GError                       **error)
{
      GPtrArray            *argv;
      NautilusBurnMediaType media_type;
      char                 *blank_str, *dev_str;
      gboolean              is_locked;
      gboolean              can_rewrite;
      gboolean              is_blank;
      int                   result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;

      recorder->priv->can_rewrite = nautilus_burn_drive_can_rewrite (drive);

      if (!recorder->priv->can_rewrite) {
            return NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      is_locked = nautilus_burn_drive_lock (drive, _("Blanking CD"), NULL);

      media_type = nautilus_burn_drive_get_media_type_full (drive, &can_rewrite, &is_blank, NULL, NULL);

      if (media_type == NAUTILUS_BURN_MEDIA_TYPE_ERROR || can_rewrite == FALSE) {
            if (is_locked)
                  nautilus_burn_drive_unlock (drive);

            return NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      argv = g_ptr_array_new ();
      g_ptr_array_add (argv, "cdrecord");

      dev_str = g_strdup_printf ("dev=%s", nautilus_burn_drive_get_device (drive));
      g_ptr_array_add (argv, dev_str);
      g_ptr_array_add (argv, "-v");

      blank_str = g_strdup_printf ("blank=%s",
                             (type == NAUTILUS_BURN_RECORDER_BLANK_FAST) ? "fast" : "all");
      g_ptr_array_add (argv, blank_str);

      if (flags & NAUTILUS_BURN_RECORDER_BLANK_DUMMY_WRITE) {
            g_ptr_array_add (argv, "-dummy");
      }

      g_ptr_array_add (argv, NULL);

      result = nautilus_burn_recorder_run_process (recorder,
                                         NAUTILUS_BURN_RECORDER_MEDIA_CD,
                                         argv,
                                         cdrecord_blank_stdout_line,
                                         cdrecord_stderr_line,
                                         error);

      if (is_locked) {
            nautilus_burn_drive_unlock (drive);
      }

      g_free (dev_str);
      g_free (blank_str);
      g_ptr_array_free (argv, TRUE);

      return result;
}

static int
nautilus_burn_recorder_blank_disc_dvdrw_format (NautilusBurnRecorder          *recorder,
                                    NautilusBurnDrive             *drive,
                                    NautilusBurnRecorderBlankType  type,
                                    NautilusBurnRecorderBlankFlags flags,
                                    GError                       **error)
{
      NautilusBurnMediaType       media_type;
      NautilusBurnProcessLineFunc out_line_func = NULL;
      NautilusBurnProcessLineFunc err_line_func = NULL;
      GPtrArray                  *argv           = NULL;
      char                       *blank_str      = NULL;
      char                       *dev_str        = NULL;
      gboolean                    is_locked;
      gboolean                    can_rewrite;
      gboolean                    is_blank;
      int                         result         = NAUTILUS_BURN_RECORDER_RESULT_ERROR;

      recorder->priv->can_rewrite = nautilus_burn_drive_can_rewrite (drive);

      if (!recorder->priv->can_rewrite) {
            return NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      is_locked = nautilus_burn_drive_lock (drive, _("Blanking DVD"), NULL);

      media_type = nautilus_burn_drive_get_media_type_full (drive, &can_rewrite, &is_blank, NULL, NULL);

      if (media_type == NAUTILUS_BURN_MEDIA_TYPE_ERROR || can_rewrite == FALSE) {
            if (is_locked)
                  nautilus_burn_drive_unlock (drive);

            return NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      if (media_type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW
          && type == NAUTILUS_BURN_RECORDER_BLANK_FULL) {
            argv = g_ptr_array_new ();

            g_ptr_array_add (argv, "growisofs");
            g_ptr_array_add (argv, "-Z");

            dev_str = g_strdup_printf ("%s=%s", nautilus_burn_drive_get_device (drive), "/dev/zero");
            g_ptr_array_add (argv, dev_str);
            g_ptr_array_add (argv, NULL);

            out_line_func = growisofs_blank_stdout_line;
            err_line_func = growisofs_stderr_line;
      } else if (!is_blank && media_type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW) {
            if (recorder->priv->debug) {
                  g_print ("Skipping fast blank for already formatted DVD+RW media\n");
            }
      } else {
            argv = g_ptr_array_new ();

            g_ptr_array_add (argv, "dvd+rw-format");

            /* undocumented option to show progress */
            g_ptr_array_add (argv, "-gui");

            if (media_type != NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW) {
                  blank_str = g_strdup_printf ("-blank%s", (type == NAUTILUS_BURN_RECORDER_BLANK_FAST) ? "" : "=full");
                  g_ptr_array_add (argv, blank_str);
            }

            dev_str = g_strdup_printf ("%s", nautilus_burn_drive_get_device (drive));
            g_ptr_array_add (argv, dev_str);
            g_ptr_array_add (argv, NULL);

            err_line_func = dvdrw_format_stderr_line;
      }

      if (argv) {
            result = nautilus_burn_recorder_run_process (recorder,
                                               NAUTILUS_BURN_RECORDER_MEDIA_DVD,
                                               argv,
                                               out_line_func,
                                               err_line_func,
                                               error);
            g_free (dev_str);
            g_free (blank_str);
            g_ptr_array_free (argv, TRUE);
      }

      if (is_locked) {
            nautilus_burn_drive_unlock (drive);
      }

      return result;
}

static gboolean
nautilus_burn_drive_format_needs_growisofs (NautilusBurnDrive    *drive,
                                  NautilusBurnMediaType type)
{
      if (can_burn_dvds (drive) == FALSE) {
            return FALSE;
      }

      if (type == NAUTILUS_BURN_MEDIA_TYPE_DVDRW
          || type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW) {
            return TRUE;
      }

      return FALSE;
}

/**
 * nautilus_burn_recorder_blank_disc:
 * @recorder: #NautilusBurnRecorder
 * @drive: #NautilusBurnDrive to use
 * @type: #NautilusBurnRecorderBlankType
 * @flags: #NautilusBurnRecorderBlankFlags
 * @error: return location for errors
 *
 * Blank the media in the specified drive.
 *
 * Return value: #NautilusBurnRecorderResult
 **/
int
nautilus_burn_recorder_blank_disc (NautilusBurnRecorder          *recorder,
                           NautilusBurnDrive             *drive,
                           NautilusBurnRecorderBlankType  type,
                           NautilusBurnRecorderBlankFlags flags,
                           GError                       **error)
{
      NautilusBurnMediaType media_type;
      gboolean              can_rewrite;
      gboolean              is_blank;

      g_return_val_if_fail (recorder != NULL, NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      g_return_val_if_fail (drive != NULL, NAUTILUS_BURN_RECORDER_RESULT_ERROR);

      media_type = nautilus_burn_drive_get_media_type_full (drive, &can_rewrite, &is_blank, NULL, NULL);

      if (media_type == NAUTILUS_BURN_MEDIA_TYPE_ERROR || media_type == NAUTILUS_BURN_MEDIA_TYPE_BUSY) {
            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      } else if (can_rewrite == FALSE) {
            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      }

      if (nautilus_burn_drive_format_needs_growisofs (drive, media_type)) {
            return nautilus_burn_recorder_blank_disc_dvdrw_format (recorder, drive, type, flags, error);
      } else {
            return nautilus_burn_recorder_blank_disc_cdrecord (recorder, drive, type, flags, error);
      }
}

static void
nautilus_burn_recorder_finalize (GObject *object)
{
      NautilusBurnRecorder *recorder = NAUTILUS_BURN_RECORDER (object);

      g_return_if_fail (object != NULL);

      if (recorder->priv->process) {
            nautilus_burn_process_free (recorder->priv->process);
      }

      if (G_OBJECT_CLASS (nautilus_burn_recorder_parent_class)->finalize != NULL) {
            (* G_OBJECT_CLASS (nautilus_burn_recorder_parent_class)->finalize) (object);
      }
}

static void
nautilus_burn_recorder_init (NautilusBurnRecorder *recorder)
{
      recorder->priv = NAUTILUS_BURN_RECORDER_GET_PRIVATE (recorder);
}

/**
 * nautilus_burn_recorder_new:
 *
 * Create a new #NautilusBurnRecorder.
 *
 * Return value: The new recorder.
 **/
NautilusBurnRecorder *
nautilus_burn_recorder_new (void)
{
      return g_object_new (NAUTILUS_BURN_TYPE_RECORDER, NULL);
}

static void
nautilus_burn_recorder_class_init (NautilusBurnRecorderClass *klass)
{
      GObjectClass *object_class;

      object_class = (GObjectClass *) klass;

      object_class->finalize = nautilus_burn_recorder_finalize;

      g_type_class_add_private (klass, sizeof (NautilusBurnRecorderPrivate));

      /* Signals */
      nautilus_burn_recorder_table_signals [PROGRESS_CHANGED] =
            g_signal_new ("progress-changed",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnRecorderClass,
                                     progress_changed),
                        NULL, NULL,
                        nautilus_burn_recorder_marshal_VOID__DOUBLE_LONG,
                        G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_LONG);
      nautilus_burn_recorder_table_signals [ACTION_CHANGED] =
            g_signal_new ("action-changed",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnRecorderClass,
                                     action_changed),
                        NULL, NULL,
                        nautilus_burn_recorder_marshal_VOID__INT_INT,
                        G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
      nautilus_burn_recorder_table_signals [ANIMATION_CHANGED] =
            g_signal_new ("animation-changed",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnRecorderClass,
                                     animation_changed),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__BOOLEAN,
                        G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
      nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST] =
            g_signal_new ("insert-media-request",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnRecorderClass,
                                     insert_cd_request),
                        NULL, NULL,
                        nautilus_burn_recorder_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEAN,
                        G_TYPE_BOOLEAN, 3, G_TYPE_BOOLEAN,
                        G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
      nautilus_burn_recorder_table_signals [WARN_DATA_LOSS] =
            g_signal_new ("warn-data-loss",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnRecorderClass,
                                     warn_data_loss),
                        NULL, NULL,
                        nautilus_burn_recorder_marshal_INT__VOID,
                        G_TYPE_INT, 0);
}

Generated by  Doxygen 1.6.0   Back to index