/*
 *	io.c
 *
 *	All the functions that interact with hardware and subprocesses
 *	and file IO. 
 *	28.3.99 tn
 *
 *
 *  Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
 *
 *  This file is part of xcdroast.
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "largefile.h"
#include "gettext.h"

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
#if defined(linux)
# include <alsa/asoundlib.h>
#endif
#if defined(__FreeBSD__)
# include <sys/soundcard.h>
# include <sys/ioctl.h>
#endif
#if defined(sun) || defined(__OpenBSD__)
# include <sys/ioctl.h>
# include <sys/audioio.h>
#endif
#ifdef aix
#include <sys/audio.h>
#endif
#ifdef hpux 
# ifndef hpux_alib
#  include <sys/audio.h>
# endif
#endif
#if defined(__sgi)
#include <dmedia/audio.h>
#endif

#if ENABLE_NLS
# define _(String) gettext (String)
# define N_(String) gettext_noop (String)
#else
# define _(String) (String)
# define N_(String) (String)
#endif
#define GTK_ENABLE_BROKEN

#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib.h>
#include "xcdrdata.h"
#include "xcdroast.h"
#include "main.h"

writerreader_devices_t **writerreaderdevs;
gchar **alt_scsidevices;
cd_info_t cdinfo;
track_info_t **trackinfo;
writer_driver_t **drivers;
writer_driver_t **blankmodes;
write_track_param_t writeparams;
gchar xcdroast_version_loaded[MAXLINE];
gint read_done;
gint read_abort_mark;

static gint scsicount;
static gint busnr;
static gint tocstate;
static gint tocnr;
static gint drvcount;
static gint dfrun;
static gint readcdda_callback;
static gint readcdda_callback2;
static gint readcdda_callback3;
static gint read_output_ctrl;
static gint read_tracknr;
static gchar readtrack_info_string[1024];
static gfloat pct_so_far, pct_this_track;
static pid_t mkisofs_pid;
static pid_t readcdda_pid, readcdda_pid2;
static gint readcd_startsector, readcd_endsector;
static gint readerr_count;
static gint matchnr;
static gint writecompleted;
static gint cddb_in;
static gchar cdinfo_cddb_title_bak[MAXLINE];
static gint cd_is_still_the_same;
static gint cdrecord_stdin, cdrecord_reload;
static gint delete_count, delete_start, delete_all;
static gint msinfo_nr1, msinfo_nr2;
static gint cdrtimer, count_mmaperror, checkmedium_found;
static time_t cdrcmdtimer;
static GtkWidget *getcdtoc_atapi_timeout_dialog;

extern gint debug;
extern setup_data_t setupdata;
extern track_read_set_t trackreadset;
extern current_set_t curset;
extern gchar *system_platform;
extern GtkWidget *toplevel;
extern GtkWidget *viewmode_dialog; 
extern GtkWidget *readtrack_info_label, *readtrack_textview;
extern GtkWidget *readtrack_pbar1, *readtrack_pbar2, *readtrack_pbar3;
extern GtkWidget *readtrack_pbar4, *readtrack_spd;
extern GtkWidget *readtrack_small_info, *readtrack_small_info2;
extern gchar hostname[MAXLINE];
extern gchar username[MAXLINE];
extern gchar sharedir[MAXLINE];
extern gchar configdir[MAXLINE];
extern gchar prefixdir[MAXLINE];
extern GtkWidget *cddb_info_label;
extern GtkCList *cddb_clist;
extern master_param_t masterparam;
extern gchar **charset_types;
extern gchar *master_fname1;
extern gchar *master_fname2;

static gboolean verify_readcd_err(GIOChannel *source, GIOCondition cond, gpointer data);
static void read_write_idle_loop(pid_t cdrecord_pid);
static gboolean read_write_out(GIOChannel *source, GIOCondition cond, gpointer data);
static gint getdevicecap(gchar *dev, gint *readmax, gint *cdrmax, gint *dvdmax, gint *iscdr, gint *isdvdrom, gint *isdvdr, gint *drvbuffer);
static gint check_medium_loaded(gint devnr);
static gint get_cdrecord_toc(gint devnr);
static void remove_tmp_writetracks_tocfile(gchar *tocfile);
static void edit_xinf_for_cd_text2(gchar *infname, gchar *title, gchar *artist, gchar *cdtitle, gchar *cdartist);
static pid_t full_dpl_pipe3(gint *out, gint *in, gint *err, gchar *cmd, 
	gint use_socketpair);
static void scroll_to_text_end(GtkWidget *txt);
static gint get_wait_ret(pid_t pid);


/*
 * convert device-type-names back to numeric
 * -1 means unknown
 */
static gint get_scsi_type(gchar *type) {

	if (strcmp(type,"Disk") == 0) {
		return 0;
	}
	if (strcmp(type,"Tape") == 0) {
		return 1;
	}
	if (strcmp(type,"Printer") == 0) {
		return 2;
	}
	if (strcmp(type,"Processor") == 0) {
		return 3;
	}
	if (strcmp(type,"WORM") == 0) {
		return 4;
	}
	if (strcmp(type,"CD-ROM") == 0) {
		return 5;
	}
	if (strcmp(type,"Scanner") == 0) {
		return 6;
	}
	if (strcmp(type,"Optical Storage") == 0) {
		return 7;
	}
	if (strcmp(type,"Juke Box") == 0) {
		return 8;
	}
	if (strcmp(type,"Communication") == 0) {
		return 9;
	}

	return -1;
}

/*
 * construct functions which are used in the invocations of the cdrtools
 */
static gint get_cur_audioread_speed() {
gint i;
	i = get_writerreaderdevs_index(curset.reader_devnr);
	return (writerreaderdevs[i]->audioread_speed);
}

static gint get_cur_audioread_overlap() {
gint i;
	i = get_writerreaderdevs_index(curset.reader_devnr);
	return (writerreaderdevs[i]->audioread_overlap);
}

static gint get_cur_audioread_showhiddentrack() {
gint i;
	i = get_writerreaderdevs_index(curset.reader_devnr);
	return (writerreaderdevs[i]->audioread_showhiddentrack);
}

static gint get_cur_audioread_useparanoia() {
gint i;
	i = get_writerreaderdevs_index(curset.reader_devnr);
	return (writerreaderdevs[i]->audioread_useparanoia);
}

static gint get_cur_audioread_paranoiaretries() {
gint i;
	i = get_writerreaderdevs_index(curset.reader_devnr);
	return (writerreaderdevs[i]->audioread_paranoiaretries);
}

static gint get_cur_audioread_paranoiareadahead() {
gint i;
	i = get_writerreaderdevs_index(curset.reader_devnr);
	return (writerreaderdevs[i]->audioread_paranoiareadahead);
}

static gint get_cur_audioread_paranoiaminoverlap() {
gint i;
	i = get_writerreaderdevs_index(curset.reader_devnr);
	return (writerreaderdevs[i]->audioread_paranoiaminoverlap);
}

static gint get_cur_writer_speed() {
gint i;
	i = get_writerreaderdevs_index(curset.writer_devnr);
	return (writerreaderdevs[i]->writer_speed);
}

static gint get_cur_writer_fifo() {
gint i;
	i = get_writerreaderdevs_index(curset.writer_devnr);
	return (writerreaderdevs[i]->writer_fifo);
}

static gint get_cur_writemode() {
gint i;
	i = get_writerreaderdevs_index(curset.writer_devnr);
	return (writerreaderdevs[i]->writer_mode);
}

static gint get_cur_writer_drvmode() {
gint i;
	i = get_writerreaderdevs_index(curset.writer_devnr);
	return (writerreaderdevs[i]->writer_drvmode);
}

 
/*
 * look in given string for "' '" and return index to it
 * return -1 if not found
 */
static gint look_for_scan_delimitor(gchar *str) {
gint i;

	for(i = 0; i < strlen(str)-2; i++) {

		if (str[i] == '\'' && str[i+1] == ' ' && str[i+2] == '\'') {
			return i;
		}
	}
	return -1;
}


/*
 * look in given string for "' " and return index to it
 * return -1 if not found
 */
static gint look_for_scan_delimitor2(gchar *str) {
gint i;

	for(i = 0; i < strlen(str)-1; i++) {

		if (str[i] == '\'' && str[i+1] == ' ') {
			return i;
		}
	}
	return -1;
}


/*
 * interpret output of -scanbus and sort into memory structure
 * if idx != -1 then we run in manual-device-mode
 */
static void parse_scan(gchar *line, gint idx, gint transport, scsi_devices_t **scsidevices) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p1, *p2;
gint s_id;
gchar s_vendor[9];
gchar s_model[17];
gchar s_rev[5];
gint s_removable;
gint s_type;
gint next;

	/* skip header */
	if (strncmp(line,"Cdrecord",8) == 0) {
		return;
	}

       if (strncmp(line,"scsibus",7) == 0 ) {
                /* set current scsibus nr */
                strcpy(tmp,line+7);
                p1=strtok(tmp,":");
                strcpy(tmp2,p1);
                /* now tmp2 contains the current busnr as string */
                busnr = atoi(tmp2);
                return;
        }

	/* a line with an error message? */
	strcpy(tmp,"cdrecord: ");
	if (strncmp(line, tmp, strlen(tmp)) == 0) {
		return;
	}

	/* a line with device info found - by checking for ")" */
	p1 = index(line,')');
	if (p1 != NULL && !strstr(line,")\"") && 
	   (strstr(line,"\t") || line[0] == ' ')) {
		strip_string(line); 

		/* get scsi-id */
		p1=strtok(line,")");
		if (!p1) return;

		strcpy(tmp,p1);
		strcpy(tmp2,p1);
		/*
		 * look for last tab or last space in tmp
		 * and remove everything before
		 */
		p1=rindex(tmp2,'\t');
		p2=rindex(tmp2,' ');
		if (p1 > p2) {
			/* last interesting char was a tab - cut here */
			if (p1 != NULL) {
				strcpy(tmp,p1+1);
			}	
		} else {
			/* last interesting char was a space - cut here */
			if (p2 != NULL) {
				strcpy(tmp,p2+1);
			}	
		}
		s_id = atoi(tmp);	
	
		/* strip off host-id from scsi-id-number */
		s_id = s_id % 100;

		p1=strtok(NULL,"'");
		if (p1 == NULL) {
			g_warning("cdrecord scanbus error: Try to remove all media from your disc drives before starting X-CD-Roast.\n");
			return;
		}

		strcpy(tmp,p1);
		strip_string(tmp);

		if (*tmp == '*') {
			/* no device found */
			return;
		}

		if (*tmp == 'H') {
			/* HOST ADAPTER found -
			   treat as no device for now */
			return;
		}

		/* get full rest of line */
		p1=strtok(NULL,"");
		if (!p1) return;
		strcpy(tmp,p1);

		if (*tmp == '\'') {
			/* empty device found? ignore */
			return;
		}

		/* e.g. tmp=YAMAHA  ' 'CRW8424S        ' '1.0j' */ 
		/* get vendor */
		next = look_for_scan_delimitor(tmp);
		if (next < 0) {
			g_error("cdrecord -scanbus output syntax error\n");
		}

		strcpy(s_vendor,"        ");
		if (next <= 8) {
			strncpy(s_vendor,tmp,next);
		} else {
			/* strip off if too long */
			strncpy(s_vendor,tmp,8);
		}	
		s_vendor[8] = '\0';

		/* get model */
		strcpy(tmp2,tmp+next+3);
		strcpy(tmp,tmp2);
		next = look_for_scan_delimitor(tmp);
		if (next < 0) {
			g_error("cdrecord -scanbus output syntax error\n");
		}
		strcpy(s_model,"                ");
		if (next <= 16) {
			strncpy(s_model,tmp,next);
		} else {
			strncpy(s_model,tmp,16);
		}
		s_model[16] = '\0';

		/* get revision */
		strcpy(tmp2,tmp+next+3);
		strcpy(tmp,tmp2);
		next = look_for_scan_delimitor2(tmp);
		if (next < 0) {
			g_error("cdrecord -scanbus output syntax error\n");
		}
		strcpy(s_rev,"    ");
		if (next <= 4) {
			strncpy(s_rev,tmp,next);
		} else {
			strncpy(s_rev,tmp,4);
		}
		s_rev[4] = '\0';

		/* get type */
		strcpy(tmp2,tmp+next+2);
		strcpy(tmp,tmp2);
		strip_string(tmp);

		if (strncmp(tmp,"Removable",9) == 0) {
			s_removable = 1;
			strcpy(tmp2,tmp+10);
			strcpy(tmp,tmp2);
		} else {
			s_removable = 0;
		}
		
		s_type = get_scsi_type(tmp);

		/* allocate and fill structure */
		scsidevices[scsicount]=g_new(scsi_devices_t,1);
		if (idx == -1) {
			/* autoscan mode */
			scsidevices[scsicount]->devnr = 
				transport*1024+busnr*32+s_id;
			scsidevices[scsicount]->alt_dev = -1;
		} else {
			/* manual mode */
			scsidevices[scsicount]->devnr = idx;
			scsidevices[scsicount]->alt_dev = idx;
		}

		scsidevices[scsicount]->sector_size = DATASECTORSIZE;
		scsidevices[scsicount]->transport = transport;
		scsidevices[scsicount]->bus = busnr;
		scsidevices[scsicount]->id = s_id;
		strcpy(scsidevices[scsicount]->vendor,s_vendor);
		strcpy(scsidevices[scsicount]->model,s_model);
		strcpy(scsidevices[scsicount]->rev,s_rev);
		scsidevices[scsicount]->removable = s_removable;
		scsidevices[scsicount]->type = s_type;
		
		scsicount++;
		if (scsicount >= MAXDEVICES) {
			g_error("Error: More than %d devices scanned\n",MAXDEVICES);
		}
	}
}


static void print_writerreaderdevs() {
gint count;

	dodebug(2,"------ cdrecord writerreaderdevs-structure -----\n");
	count = 0;
	while(writerreaderdevs[count] != NULL) {
		dodebug(2, "devnr=%d (%s) [%s %s %s] (ssize: %d)\n", 
			writerreaderdevs[count]->devnr,
			writerreaderdevs[count]->devicestr,
			writerreaderdevs[count]->vendor,
			writerreaderdevs[count]->model,
			writerreaderdevs[count]->rev,
			writerreaderdevs[count]->sector_size);
		dodebug(2, "  readmax: %d, cdrmax: %d, dvdmax: %d, is_cdrwriter: %d, is_dvdreader: %d, is_dvdwriter: %d\n",
			writerreaderdevs[count]->writer_readmaxspeed,
			writerreaderdevs[count]->writer_cdrmaxspeed,
			writerreaderdevs[count]->writer_dvdmaxspeed,
			writerreaderdevs[count]->is_cdrwriter,
			writerreaderdevs[count]->is_dvdreader,
			writerreaderdevs[count]->is_dvdwriter,
			writerreaderdevs[count]->sector_size);
		dodebug(2, "  writer_flags: %s\n", 
			writerreaderdevs[count]->writer_flags);
		dodebug(2, "  writer_modes: %s\n", 
			writerreaderdevs[count]->writer_modes);
			
		count++;
	}
}


/*
 * create a new writer/reader entry
 */
static gint add_writerreader(scsi_devices_t *scsidev, gchar *transport, GtkWidget *txt) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar drvflags[MAXLINE], drvmodes[MAXLINE];
gint readmax, cdrmax, dvdmax, iscdr, isdvdr, isdvdrom, drvbuffer;
gint count;
gint mmap_error;
gint i;
static const gchar *writemodes[] = WRITE_MODES;

	/* first search last entry of writerreader struct */
	count = 0;
	while(writerreaderdevs[count] != NULL) {
		if (count == MAXDEVICES-1) {
			return 0;
		}
		count++;
	}

	/* only add type WORM and CD-ROM types */
	if (scsidev->type != 4 && scsidev->type != 5) {
		return 0;
	}

	/* build device string */
	if (scsidev->alt_dev == -1) {
		g_snprintf(tmp,MAXLINE,"%s%d,%d,%d",
			transport, scsidev->bus, scsidev->id, 0);
	} else {
		strncpy(tmp, transport, MAXLINE);
	}	

	/* check if this device already exists */
	if (get_writerreaderdevs_index_from_devstr(tmp) != -1) {
		if (txt) {
			g_snprintf(tmp2,MAXLINE,"([%s] %s %s %s)\n",
				tmp, scsidev->vendor,
				scsidev->model, scsidev->rev);
			append_to_text_view(txt, tmp2);
                	wait_and_process_events();      
		}
		return 0;
	}

	/* create a new entry */
	writerreaderdevs[count]=g_new0(writerreader_devices_t,1);
	writerreaderdevs[count]->devnr = scsidev->devnr;
	writerreaderdevs[count]->devicestr = g_strdup(tmp);
	strncpy(writerreaderdevs[count]->vendor, scsidev->vendor,9);
	strncpy(writerreaderdevs[count]->model, scsidev->model,17);
	strncpy(writerreaderdevs[count]->rev, scsidev->rev,5);
	writerreaderdevs[count]->sector_size = scsidev->sector_size;

	getmodesflags(tmp, drvflags, drvmodes); 
	writerreaderdevs[count]->writer_flags = g_strdup(drvflags);
	writerreaderdevs[count]->writer_modes = g_strdup(drvmodes);

	/* get more detailed information about this device */
        mmap_error=getdevicecap(tmp, &readmax, &cdrmax, &dvdmax, &iscdr, &isdvdrom, &isdvdr, &drvbuffer);
	writerreaderdevs[count]->writer_readmaxspeed = readmax;
	writerreaderdevs[count]->writer_cdrmaxspeed = cdrmax;
	writerreaderdevs[count]->writer_dvdmaxspeed = dvdmax;
	writerreaderdevs[count]->is_cdrwriter = iscdr;
	writerreaderdevs[count]->is_dvdreader = isdvdrom;
	writerreaderdevs[count]->is_dvdwriter = isdvdr;
	

	/* workaround for non-mcc drives */
	if (writerreaderdevs[count]->is_cdrwriter == 0) {
		/* not detected as writer, but got write modes? */
		if (strlen(drvmodes) > 0) {
			/* assume its really a writer then */
			writerreaderdevs[count]->is_cdrwriter = 1;
		}
	}

	/* set defaults for writer fields */
	writerreaderdevs[count]->writer_drvmode = -1;
	writerreaderdevs[count]->writer_speed = cdrmax;
	writerreaderdevs[count]->writer_fifo = 8;

	/* get the first supported mode for that writer */
	i = 0;
	while (writemodes[i]) {
		if (writemode_supported(i, scsidev->devnr)) {
			writerreaderdevs[count]->writer_mode = i; 
			break;
		}	
		i++;
	}

	/* set defaults for reader fields */
	writerreaderdevs[count]->audioread_speed = readmax;
	writerreaderdevs[count]->audioread_overlap = 1;
	writerreaderdevs[count]->audioread_showhiddentrack = 1;
	writerreaderdevs[count]->audioread_useparanoia = 1;
	writerreaderdevs[count]->audioread_paranoiaretries = 20;
	/* number of sectors for audio readahead buffer > drive buffer (1 audio sector = 2448 bytes) */
	writerreaderdevs[count]->audioread_paranoiareadahead = (gint) (drvbuffer / 244800 + 1) * 100;
	writerreaderdevs[count]->audioread_paranoiaminoverlap = 0;

	/* add to dialog window */
        if (txt) {
		g_snprintf(tmp2,MAXLINE,"[%s] %s %s %s\n",
			tmp, scsidev->vendor,
			scsidev->model, scsidev->rev);
		append_to_text_view(txt, tmp2);
                wait_and_process_events();      
	}

	return mmap_error;
}


/*
 * does scan for a single device only
 * return 1 when new device was found
 */
static gint scanbus_single(gchar *dev, gint newdevnr, GtkWidget *txt) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
FILE *fpin;
scsi_devices_t **scsidevices;
gint found;

	scsidevices = g_new0(scsi_devices_t *, MAXDEVICES);
	scsicount = 0;

	get_wrap_path("CDRECORD", line);

	/* make sure we escape any critical chars in input */
	strncpy(tmp2,dev,MAXLINE);
	convert_escape(tmp2);

	g_snprintf(tmp,MAXLINE," -scanbus dev= \"%s\" 2>&1",tmp2);
	strcat(line,tmp);

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10,"scanbus: %s",line);
                parse_scan(line, newdevnr, 0, scsidevices);
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	found = 0;
	if (scsidevices[0]) {
		add_writerreader(scsidevices[0], dev, txt);
		g_free(scsidevices[0]);
		found = 1;
	}
	g_free(scsidevices);

	return found;
}


/*
 * scanbus scsi
 */
static void scan_traditional(GtkWidget *txt) {
gchar line[MAXLINE];
FILE *fpin;
gint count, errcount, ret;
scsi_devices_t **scsidevices;

	scsidevices = g_new0(scsi_devices_t *, MAXDEVICES);
	scsicount = 0;

	/* traditional scanning first */
	get_wrap_path("CDRECORD", line);
	strcat(line," -scanbus 2>&1");

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10,"scanbus: %s",line);
                parse_scan(line, -1, 0, scsidevices);
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	/* add the found devices to the writerreader structure */
	count = 0;
	errcount = 0;
	while(scsidevices[count] != NULL) {
		errcount+=add_writerreader(scsidevices[count],"", txt);
		g_free(scsidevices[count]);
		count++;
	}
	g_free(scsidevices);

	/* historic mmap error check */
	if (errcount > 0) {
                ret = show_dialog(ICO_WARN, _("Warning: The cdrtools binaries you have installed are\nnot compatible with your system. You will NOT be\nable to read or write CDs. Please read the FAQ\non www.xcdroast.org for more information."), T_ANYWAY, _("Exit"), NULL, 1);
                if (ret == 1)
                        gtk_exit(1);
	}
}


/*
 * alternative transport method scanning
 * valid transport choices for now: ATAPI USCSI REMOTE
 */
static gint scan_other(gchar *transport, gint transid, GtkWidget *txt) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
FILE *fpin;
gint count;
scsi_devices_t **scsidevices;

	scsidevices = g_new0(scsi_devices_t *, MAXDEVICES);
	scsicount = 0;

	get_wrap_path("CDRECORD", line);
	g_snprintf(tmp,MAXLINE," dev=%s -scanbus 2>&1", transport);
	strcat(line, tmp);
	
	dodebug(1, "calling: %s\n", line);
       	if ((fpin = popen(line,"r")) == NULL) {
               	 g_error("popen error\n");
       	}

        for (;;) {
         	if (fgets(line,MAXLINE,fpin) == NULL) 
                       	break;
		dodebug(10,"scanbus (%s): %s", transport, line);
               	parse_scan(line, -1, transid, scsidevices);
       	}

       	if (pclose(fpin) == -1) {
               	g_error("pclose error\n");
       	}

	/* add the found devices to the writerreader structure */
	count = 0;
	g_snprintf(tmp,MAXLINE,"%s:", transport);
	while(scsidevices[count] != NULL) {
		add_writerreader(scsidevices[count], tmp, txt);
		g_free(scsidevices[count]);
		count++;
	}
	g_free(scsidevices);

	return count;
}


/*
 * new scanbus version
 */
void scanbus_new(GtkWidget *txt, gint scanparam) {
gchar tmp[MAXLINE];
gint doscanbus;
gint i, devnr;

	/* allocate memory */
	if (!writerreaderdevs) {
		writerreaderdevs = g_new0(writerreader_devices_t *, MAXDEVICES);
	}

	doscanbus = 1;

	/* output status texts? */
	if (txt) {
		strncpy(tmp,_("Starting to scan for devices...\n"), MAXLINE);
		append_to_text_view_bold(txt, tmp);
		wait_and_process_events();	
	}

	/* check if we have to do full scanbus or only partial */
	if (alt_scsidevices[0] != NULL) {
		i = 0;
		while (alt_scsidevices[i] != NULL) {
			/* look for a free device number (greater 8192) */
			for (devnr = 8192; devnr < 8192 + MAXDEVICES; devnr++) {
				if (get_writerreaderdevs_index(devnr) == -1) {
					break;
				}	
			}	
			scanbus_single(alt_scsidevices[i], devnr, txt);
			i++;
		}		

		/* trigger not to scan the bus any futher */
		doscanbus = 0;
	}

	if (doscanbus) {
		/* scan for scsi devices */
		scan_traditional(txt);

		/* scan for alternatives */
#if defined(linux)
		if (scanparam) {
			scan_other("ATA", 1, txt);
		}
#endif
#if defined(sun)
		if (scanparam) {
			scan_other("USCSI", 2, txt);
		}
#endif
	}

	if (txt) {
		strncpy(tmp,_("Scan finished.\n"), MAXLINE);
		append_to_text_view_bold(txt, tmp);
		wait_and_process_events();	
	}

	if (debug) print_writerreaderdevs();
}



/*
 * scan for remote scsi devices
 */
void scanbus_rscsi(gchar *devstr, GtkWidget *txt) {
gchar tmp[MAXLINE];
gint found;
gint i, highest;

	/* allocate memory */
	if (!writerreaderdevs) {
		writerreaderdevs = g_new0(writerreader_devices_t *, MAXDEVICES);
	}

	/* output status texts? */
	if (txt) {
		strncpy(tmp,_("Starting to scan for remote devices...\n"), MAXLINE);
		append_to_text_view_bold(txt, tmp);
		wait_and_process_events();	
	}

	/* look for highest free devnr - but at least a multiple of 16384 */
	i = 0;
	highest = -1;
	while(writerreaderdevs[i] != NULL) {
		if (writerreaderdevs[i]->devnr > highest) {
			highest = writerreaderdevs[i]->devnr;
		}
		i++;
	}
	highest++;

	/* when we scan multiple times always arange a new block to play safe */
	highest = ((highest/16384)+1)*16384;

	found = scan_other(devstr, highest/1024, txt);

	if (txt) {
		if (found == 0) {
			strncpy(tmp,_("No devices found - check your remote scsi setup.\n"), MAXLINE);
			append_to_text_view(txt, tmp);
		}
		strncpy(tmp,_("Scan finished.\n"), MAXLINE);
		append_to_text_view_bold(txt, tmp);
		wait_and_process_events();	
	}

	if (debug) print_writerreaderdevs();
}


/*
 * special version where we scan only for a single device
 */
void scanbus_new_single(gchar *devstr, GtkWidget *txt) {
gchar tmp[MAXLINE];
gint devnr, found;

	/* allocate memory */
	if (!writerreaderdevs) {
		writerreaderdevs = g_new0(writerreader_devices_t *, MAXDEVICES);
	}

	/* output status texts? */
	if (txt) {
		strncpy(tmp,_("Starting to scan for devices...\n"), MAXLINE);
		append_to_text_view_bold(txt, tmp);
		wait_and_process_events();	
	}

	/* look for a free device number (greater 8192) */
	for (devnr = 8192; devnr < 8192 + MAXDEVICES; devnr++) {
		if (get_writerreaderdevs_index(devnr) == -1) {
			break;
		}	
	}	
	found = scanbus_single(devstr, devnr, txt);

	if (txt) {
		if (!found) {
			strncpy(tmp,_("Device not found.\n"), MAXLINE);
			append_to_text_view(txt, tmp);
		}
		strncpy(tmp,_("Scan finished.\n"), MAXLINE);
		append_to_text_view_bold(txt, tmp);
		wait_and_process_events();	
	}

	if (debug) print_writerreaderdevs();
}


/*
 * interpret output of driver=help and sort into memory structure
 */
static void parse_driver(gchar *line) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar drv[MAXLINE];
gchar *p1;
gint n;
	if (strncmp(line,"Driver types:",13) != 0 ) {

		strcpy(tmp,line);
		p1=strtok(tmp," ");
		strcpy(drv,p1);	
		/* now drv contains the driver name */

		p1=strtok(NULL,"");
		strcpy(tmp2,p1);
		strip_string(tmp2);
		/* now tmp2 contains the description */

		/* cut "driver for" away */ 
		if (strncmp(tmp2,"driver for ",11) == 0 ){
			strcpy(tmp,tmp2+11);
			strcpy(tmp2,tmp);
		}

		/* allocate structure */
		drivers[drvcount]=g_new(writer_driver_t,1);

		n = strlen(drv)+1;
		drivers[drvcount]->driver=g_new(gchar,n);
		strcpy(drivers[drvcount]->driver,drv);

		n = strlen(tmp2)+1;
		drivers[drvcount]->desc=g_new(gchar,n);
		strcpy(drivers[drvcount]->desc,tmp2);

		drvcount++;
		if (drvcount >= MAXDRIVERS) {
			g_error("Error: More than %d writer devices found\n",MAXDRIVERS);
		}
	}
}


/*
 * print memory-structure with drivers (debug purposes)
 */
static void print_drivers() {
gint count;

	dodebug(2,"------ cdrecord drivers-structure -----\n");
	count = 0;
	while(drivers[count] != NULL) {
		dodebug(2, "%s:%s\n",
			drivers[count]->driver,
			drivers[count]->desc);
			
		count++;
	}
}


/*
 * call cdrecord driver=help
 */
void scandrivers() {
gchar line[MAXLINE];
FILE *fpin;

	/* allocate memory */
	drivers = g_new0(writer_driver_t *,MAXDRIVERS);
	drvcount = 0;

	get_wrap_path("CDRECORD", line);
	strcat(line," driver=help 2>&1");

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10, "driverlist: %s",line);
                parse_driver(line);
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	if (debug) print_drivers();
}


/*
 * print memory-structure with charsets (debug purposes)
 */
static void print_charsets() {
gint count;
gchar tmp[MAXLINE];

	dodebug(2,"------ mkisofs-charsets-structure -----\n");
	count = 0;
	strcpy(tmp,"");	
	while(charset_types[count] != NULL) {

		strcat(tmp,charset_types[count]);

		if ((count+1) % 6) {
			strcat(tmp, ", ");
		} else {
			dodebug(2, "%s\n", tmp);
			strcpy(tmp,"");	
		}
		count++;
	}
	if (strcmp(tmp,"") != 0) 
		dodebug(2, "%s\n", tmp);
}


/*
 * compare charset names (for qsort)
 */
static int sort_charsets (const void *a, const void *b)
{
	return strverscmp (*(char * const *)a, *(char *const *)b);
}


/*
 * call mkisofs -input-charset help
 */
void scancharsets() {
gchar line[MAXLINE];
FILE *fpin;
gint start,count,tcount,i;
gchar **tmp_types;

	/* allocate memory */
	charset_types = g_new0(gchar *,MAXCHARSETS+1);
	tmp_types = g_new0(gchar *,MAXCHARSETS+1);

	start = 0;
	count = 0;

	get_wrap_path("MKISOFS", line);
	strcat(line," -input-charset help 2>&1");

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		/* wait for Known charsets.... line */
		if (strncmp("Known",line,5) == 0) {
			start = 1;
			continue;
		}
		if (strcmp("mkisofs: 'iconv -l' lists more available names.\n",line) == 0) 
			continue;

		if (start) {
			dodebug(10, "mkisofs charsets: %s",line);

			tmp_types[count] = g_strdup(strip_string(line));
			count++;		
			if (count >= MAXCHARSETS) {
				g_error("Error: More than %d input charsets found\n",MAXCHARSETS);
			}
		}
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	/* now sort charsets, adding a few special types into right places */
	qsort(tmp_types,count,sizeof(*tmp_types),sort_charsets);

	tcount = count;
	count = 0;
	charset_types[count] = g_strdup("default");
	count++;
	charset_types[count] = g_strdup("utf-8");
	count++;
	for (i=0;i<tcount;i++) {
		charset_types[count] = tmp_types[i];
		count++;
	}
	charset_types[count] = g_strdup("custom");
	count++;

	g_free(tmp_types);

	if (debug) print_charsets();
}


/*
 * call cdrecord -prcap to get some device information
 * also checks for mmap errors and return 1 if one occured
 */
static gint getdevicecap(gchar *dev, gint *readmax, gint *cdrmax, gint *dvdmax, gint *iscdr, gint *isdvdrom, gint *isdvdr, gint *drvbuffer) {
gchar tmp[MAXLINE];
gchar line[MAXLINE];
gint tmpnr, errcount;
FILE *fpin;
gchar *p;

	/* set reasonable defaults */
	*readmax = 0;
	*cdrmax = 0;
	*dvdmax = 0;
	*iscdr = 0;
	*isdvdrom = 0;
	*isdvdr = 0;
	*drvbuffer = 1;

        if (!dev) return 0;

        get_wrap_path("CDRECORD", tmp);
        g_snprintf(line, MAXLINE, "%s dev= \"%s\" -prcap 2>&1", tmp, dev);

        dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	errcount = 0;
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
                dodebug(10, "prcap: %s",line);

                /* line with mmap error? */
                if (strstr(line,"annot get mmap for")) {
                        errcount++;
                }

		if (strstr(line, "Does write CD-R media")) {
			*iscdr = 1;
		}
		if (strstr(line, "Does read DVD-ROM media")) {
			*isdvdrom = 1;
		}
		if (strstr(line, "Does write DVD-R media")) {
			*isdvdr = 1;
		}
		if (strstr(line, "Maximum read  speed:")) {
			p = strtok(line,"(");
			if (p) {
				p = strtok(NULL,")");
				if (p) {
					strcpy(tmp,p);
					sscanf(tmp,"CD %3dx, DVD %3d",
						readmax, &tmpnr);
				}
			}
		} else 
		if (strstr(line, "Maximum write speed:")) {
			p = strtok(line,"(");
			if (p) {
				p = strtok(NULL,")");
				if (p) {
					strcpy(tmp,p);
					sscanf(tmp,"CD %3dx, DVD %3d",
						cdrmax, dvdmax);
				}
			}
		}
		if (strstr(line, "Buffer size in KB:")) {
			p = strstr(line, "KB: ");
			if (p) {
				strcpy(tmp,p+4);
			}
			*drvbuffer = (gint) atoi(tmp) * 1024;
		}
	}

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

        if (errcount > 0) {
                return 1;
        } else {
                return 0;
        }
}


/*
 * get driverflags and modes from drive
 */
void getmodesflags(gchar *dev, gchar *drvflags, gchar *drvmodes) {
gchar tmp[MAXLINE];
gchar line[MAXLINE];
FILE *fpin;
gchar *p;

	strcpy(drvflags,"");
	strcpy(drvmodes,"");

        if (!dev) return;

        get_wrap_path("CDRECORD", tmp);
        g_snprintf(line, MAXLINE, "%s dev= \"%s\" -v -checkdrive 2>&1", tmp, dev);

        dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
                dodebug(10, "checkdrive: %s",line);

		if (strstr(line, "Driver flags   :")) {
			p = strtok(line,":");
			if (p) {
				p = strtok(NULL,"");
				if (p) {
					strncpy(tmp,p,MAXLINE);
					strip_string(tmp);
					strncpy(drvflags,tmp,MAXLINE);
				}
			}
		} else 
		if (strstr(line, "Supported modes:")) {
			p = strtok(line,":");
			if (p) {
				p = strtok(NULL,"");
				if (p) {
					strncpy(tmp,p,MAXLINE);
					strip_string(tmp);
					strncpy(drvmodes,tmp,MAXLINE);
				}
			}
		}
	}

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }
}


static gint parse_freespace(gchar *line, gchar *fs) {
gchar tmp[MAXLINE];
gchar *p1;

	/* skip first line(s) */
	if (index(line,'/')) {
	        strcpy(tmp,line);
#if defined (aix)
		/* filesystem label */
		p1=strtok(tmp," ");
		if (fs != NULL) {
		     /* get the filesystem */
		     strcpy(fs,p1);
		}
		/* size of filesystem */
		p1=strtok(NULL," ");
		if (p1 == NULL) { 
			g_error("df -k output syntax error\n");
		}
		/* available blocks */
		p1=strtok(NULL," ");
		if (p1 == NULL) { 
			g_error("df -k output syntax error\n");
		}
		return (atoi(p1));
#elif defined (hpux)
		/* mountpoint */
		p1=strtok(tmp,"(");
		if (p1 == NULL) { 
			g_error("df -b output syntax error\n");
		}
		/* filesystem label */
		p1=strtok(NULL,")");
		if (fs != NULL) {
		     /* get the filesystem - avoid "(" at the beginning */
		     strcpy(fs,p1);
		}
		/* size in kb */
		p1=strtok(NULL,": ");
		if (p1 == NULL) { 
			g_error("df -b output syntax error\n");
		}		
		return (atoi(p1));
#else

		/* skip the first 4 fields in output to come to "available" */

		/* are we handling the first line of two?*/
		p1=strtok(tmp," ");

		if (dfrun == 0) {
			if (fs != NULL) {
				/* get the filesystem */
				strcpy(fs,p1);
			}

			p1=strtok(NULL," ");
			if (p1 == NULL) {
				/* ok..output splitted on two lines */
				dfrun = 1;
				return -1;
			}
		}
		p1=strtok(NULL," ");
		if (p1 == NULL) {
			g_error("df -k output syntax error\n");
		} 
		p1=strtok(NULL," ");
		if (p1 == NULL) { 
			g_error("df -k output syntax error\n");
		}
		return (atoi(p1));
#endif
	}
	return -1;
}


/*
 * get free disk space. return in 1024byte blocks or -1 if not valid
 * return filesystem if not set to NULL
 * will handle if output is in two lines...like when the filesystem
 * output is longer that 20 chars - e.g. on nfs-links
 * will handle df-output like:
 *
 * (Solaris)
 * Filesystem         kbytes     used   avail   capacity  Mounted on
 * fileserv:/export/home
 *                  17502608 11609120 5718464        67%  /export/home
 * 
 * - OR -
 * 
 * (Linux)
 * Filesystem    1024-blocks     Used Available Capacity Mounted on
 * /dev/sda3         2494898  1606489    759428      68% /
 * 
 * Système de
 * fichiers         1K-blocs   Utilisé  Disponible  U.%  Monté sur
 * /dev/hda6        10080488   8043456     1524964  85%  /
 * 
 * - OR -
 * 
 * (AIX)
 * Filesystem    1024-blocks    Free %Used    Iused %Iused Mounted on
 * /dev/hd4             8192    2968   64%     1319    33% /
 * /dev/hd2           573440   31644   95%    22280    16% /usr
 * 
 * - OR -
 * 
 * (HP-UXs df -b)
 * /tmp             (/dev/vg00/lvol4       ) :   188158 Kbytes free
 * /usr             (/dev/vg00/lvol5       ) :   286696 Kbytes free
 * 
 */
gint get_free_space(gchar *path, gchar *filesystem) {
gchar line[MAXLINE];
FILE *fpin;
gint space;

	space = -1;
	dfrun = 0;

	if (is_directory(path) != 1) {
		return -1;
	}

	if (stat_file(DF)) {
		strcpy(line,DF);
		strcat(line," \"");
		strcat(line,path);
		strcat(line,"\"");
	} else {
		strcpy(line,DF2);
		strcat(line," \"");
		strcat(line,path);
		strcat(line,"\"");
	}

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10,"df: %s",line);
                space = parse_freespace(line,filesystem);
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	if (filesystem != NULL) {
		dodebug(2, "filesystem lookup for %s = %d blocks, fs = %s\n", 
			path, space, filesystem);
	} else {
		dodebug(2, "filesystem lookup for %s = %d blocks, fs = %s\n", 
			path, space, "(NULL)");
	}
 
	return space;
}


/*
 * get a list of all audio-devices found on a system. This has to be
 * done platform dependent
 */
GList *get_dsp_devices() {
GList *dsp;
GList *loop;
#if defined(__FreeBSD__)
struct stat buf;
#endif
#if defined(linux) 
void **hints, **n;
gchar *name;
#endif
#if defined(sun) || defined(aix) || defined(__OpenBSD__)
gchar *audiodev;
#endif
	dsp = NULL;

#if defined(linux) 
    if (snd_device_name_hint(-1, "pcm", &hints) >= 0)
    {
        n = hints;
        while (*n != NULL) {
            name = snd_device_name_get_hint(*n, "NAME");
	        /* skip some devices */
	        if (strcmp(name, "null") != 0 && strncmp(name, "front", 5) != 0 
                    && strncmp(name, "surround", 8) != 0)
	    	    dsp = g_list_append(dsp, g_strdup(name));
            free(name);
            n++;
        }
    } 
#endif

#if defined(__FreeBSD__)
	/*
	 * for linux check if /dev/dsp or /dev/dsp1 exist
	 */
	if (stat("/dev/dsp",&buf) == 0) {
		dsp = g_list_append(dsp,"/dev/dsp");
	}
	if (stat("/dev/dsp1",&buf) == 0) {
		dsp = g_list_append(dsp,"/dev/dsp1");
	}
#endif
#if defined(sun) || defined(__OpenBSD__)
	/*
	 * check if the user has any special audio-hardware running,
	 * which set the AUDIODEV-environment-variable
	 */
	audiodev = getenv("AUDIODEV");
	if (audiodev != NULL) {

		if (stat(audiodev,&buf) == 0) {
			dsp = g_list_append(dsp,g_strdup(audiodev));
		}
	} else {
		audiodev = "";
	}
	if (strcmp(audiodev,"/dev/audio") != 0) {

		if (stat("/dev/audio",&buf) == 0) {
			dsp = g_list_append(dsp,"/dev/audio");
		}
	}
#endif
#ifdef aix
       audiodev = getenv ("AUDIODEV");
       if (audiodev != NULL) {

               if (stat (audiodev, &buf) == 0) {
                       dsp = g_list_append (dsp, g_strdup(audiodev));
               }
       }
       /* Try to use the device of a machine with PCI bus */
        if (stat ("/dev/paud0/1", &buf) == 0) {
               dsp = g_list_append (dsp, "/dev/paud0/1");
       }
        /* Try to use the device of a machine with MCA bus */
        if (stat ("/dev/baud0/1", &buf) == 0) {
               dsp = g_list_append (dsp, "/dev/baud0/1");
       }
#endif
#ifdef hpux
# ifndef hpux_alib
       /*
	* for HP-UX check if /dev/audio exists
	* I've never seen other audio devices under HP-UX
	*/
       if (stat("/dev/audio",&buf) == 0) {
               dsp = g_list_append(dsp,"/dev/audio");
       }
# else
       /*
	* for HP-UX with the Alib we don't need to check if the
        * device exists - we actually do not know even the device
	*/
       dsp = g_list_append(dsp,"AUDIO ENVIRONMENT");
# endif
#endif

#if defined(__sgi)
{
	int     i, rv;
	ALvalue devs [16];
	ALpv    q [1];
	char    devLabel [32];

	/*
	 * Fetch the list of available output audio devices.
	 */
	q[0].param = AL_TYPE;
	q[0].value.i = AL_OUTPUT_DEVICE_TYPE;
	if ((rv = alQueryValues(AL_SYSTEM, AL_DEVICES, devs, 16, q, 1)) >= 0) {

		for (i = 0; i < rv; i++) {
			q[0].param = AL_LABEL;
			q[0].value.ptr = devLabel;
			q[0].sizeIn = 32;
			alGetParams(devs[i].i, q, 1);
			if (alIsSubtype(AL_DEVICE_TYPE, devs[i].i)) {
				dsp = g_list_append(dsp, g_strdup(devLabel));
			}
		}
	}
}
#endif

	/* do some debug output */
	if (debug) {
		loop = g_list_first(dsp);
		while(loop) {
			if (loop->data != NULL) 
				dodebug(10, "dspscan: %s\n", 
					(gchar *) loop->data);
			loop = loop->next;
		}
	}

	return g_list_first(dsp);
}


/*
 * take a dsp-device and find the fitting mixer-device
 */
gchar *gen_mix_from_dspdev(gchar *dsp, gchar *ret) {
gchar tmp[MAXLINE];
#if !defined(aix) && !defined(linux)
struct stat buf;
# endif
#if defined(__FreeBSD__)
gchar tmp2[MAXLINE];
#endif

	strcpy(ret,"");

#if defined(linux) 
       g_snprintf(tmp,MAXLINE,"%s",dsp);
       strcpy(ret,tmp);
#endif

#if defined(__FreeBSD__)

	if (strncmp(dsp,"/dev/dsp",8) == 0) {
		strcpy(tmp,dsp+8);
		g_snprintf(tmp2,MAXLINE,"/dev/mixer%s",tmp);

		/* does device exist? */
		if (stat(tmp2,&buf) == 0) {
			strcpy(ret,tmp2);
		}	
	}	
#endif
#if defined(sun) || defined(__OpenBSD__)

	g_snprintf(tmp,MAXLINE,"%s%s",dsp,"ctl");

		/* does device exist? */
		if (stat(tmp,&buf) == 0) {
			strcpy(ret,tmp);
		}
#endif
#ifdef aix
       /*
	* The gain will be set via ioctl
	* and the usual output device
	*/
       g_snprintf(tmp,MAXLINE,"%s",dsp);
       strcpy(ret,tmp);
#endif
#ifdef hpux
# ifndef hpux_alib
	g_snprintf(tmp,MAXLINE,"%s%s",dsp,"Ctl");

	/* does device exist? */
	if (stat(tmp,&buf) == 0) {
	        strcpy(ret,tmp);
	}
# else
	/*
	 * We have no control or mixer device
	 * -> all is done via the Aserver-daemon and its API
	 */
	strcpy(ret, "ASERVER");	
# endif
#endif

	dodebug(10, "mixer: %s\n", ret);
	return ret;
}


/*
 * call uname -a to get a nice system-id-string
 */
gchar *get_uname_info(gchar *str) {
FILE *fpin;

	if (stat_file(UNAME)) {
		dodebug(1, "calling: %s\n", UNAME);
       		if ((fpin = popen(UNAME,"r")) == NULL) {
                	g_error("popen error\n");
		}
        } else {
		dodebug(1, "calling: %s\n", UNAME2);
       		if ((fpin = popen(UNAME2,"r")) == NULL) {
                	g_error("popen error\n");
		}
	}

	strcpy(str,"");
	if (fgets(str,MAXLINE,fpin) == NULL)
                g_error("fgets error\n");

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	dodebug(10, "uname: %s\n", str);

	return str;
}


/*
 * Save the setup-configuration to a file - all strings are converted
 * in a printable form first:  return 0 if ok, or 1 on error
 */
gint save_setup_config(gchar *confdir, gchar *fname) {
FILE *fd;
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
GList *loop;
gint count;

	/* no confdir? treat path absolute */
	if (strcmp(confdir,"") == 0) {
		strncpy(tmp,fname,MAXLINE);
	} else {

		/* now check if the confdir exists */
		if (!is_directory(confdir)) {
			/* try to create directory */
			mkdir(confdir, 0700);
			dodebug(2, "trying to mkdir %s\n", confdir);
		}

		g_snprintf(tmp,MAXLINE,"%s/%s", confdir, fname);
	}

	dodebug(1, "Opening %s for writing\n", tmp);
	dolog(3, "Saving config file %s\n", tmp);

	fd = fopen(tmp,"w"); 

	if (fd == NULL) { 
		/* error opening file */
		return 1;
	}

	/* write the config-file header */
	fputs("#\n",fd);
	g_snprintf(tmp,MAXLINE,"# X-CD-Roast %s Configuration-File\n",XCDROAST_VERSION);
	fputs(tmp,fd);
	fputs("#\n",fd);
	fputs("# Automatically created by the X-CD-Roast-Setup\n",fd);
	fputs("# Don't edit! (Unless you REALLY know what you are doing)\n",fd);
	fputs("#\n\n",fd);

	/* write data */
	g_snprintf(tmp,MAXLINE,"VERSION = \"%s\"\n",XCDROAST_VERSION); 
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"PLATFORM = \"%s\"\n",system_platform); 
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITER_DEVNR = %d\n",setupdata.writer_devnr);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"READER_DEVNR = %d\n",setupdata.reader_devnr);
	fputs(tmp,fd);

	fputs("#\n",fd);

	/* write hardware structure */
	count = 0;
	while(writerreaderdevs[count] != NULL) {

		g_snprintf(tmp,MAXLINE,"WRITERREADER_DEVNR = %d\n",writerreaderdevs[count]->devnr);
		fputs(tmp,fd);

		strcpy(tmp2,writerreaderdevs[count]->devicestr);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_DEVICESTR = \"%s\"\n",convert_escape(tmp2));
		fputs(tmp,fd);

		strcpy(tmp2,writerreaderdevs[count]->vendor);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_VENDOR = \"%s\"\n",convert_escape(tmp2));
		fputs(tmp,fd);
		strcpy(tmp2,writerreaderdevs[count]->model);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_MODEL = \"%s\"\n",convert_escape(tmp2));
		fputs(tmp,fd);
		strcpy(tmp2,writerreaderdevs[count]->rev);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_REV = \"%s\"\n",convert_escape(tmp2));
		fputs(tmp,fd);
		
		g_snprintf(tmp,MAXLINE,"WRITERREADER_READMAXSPEED = %d\n",writerreaderdevs[count]->writer_readmaxspeed);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_CDRMAXSPEED = %d\n",writerreaderdevs[count]->writer_cdrmaxspeed);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_DVDMAXSPEED = %d\n",writerreaderdevs[count]->writer_dvdmaxspeed);
		fputs(tmp,fd);

		strcpy(tmp2,writerreaderdevs[count]->writer_flags);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_WRITER_FLAGS = \"%s\"\n",convert_escape(tmp2));
		fputs(tmp,fd);
		strcpy(tmp2,writerreaderdevs[count]->writer_modes);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_WRITER_MODES = \"%s\"\n",convert_escape(tmp2));
		fputs(tmp,fd);

		g_snprintf(tmp,MAXLINE,"WRITERREADER_IS_CDRWRITER = %d\n",writerreaderdevs[count]->is_cdrwriter);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_IS_DVDWRITER = %d\n",writerreaderdevs[count]->is_dvdwriter);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_IS_DVDREADER = %d\n",writerreaderdevs[count]->is_dvdreader);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_SECTOR_SIZE = %d\n",writerreaderdevs[count]->sector_size);
		fputs(tmp,fd);

		g_snprintf(tmp,MAXLINE,"WRITERREADER_DRVMODE = %d\n",writerreaderdevs[count]->writer_drvmode);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_MODE = %d\n",writerreaderdevs[count]->writer_mode);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_SPEED = %d\n",writerreaderdevs[count]->writer_speed);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_FIFO = %d\n",writerreaderdevs[count]->writer_fifo);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_AUDIOREAD_SPEED = %d\n",writerreaderdevs[count]->audioread_speed);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_AUDIOREAD_OVERLAP = %d\n",writerreaderdevs[count]->audioread_overlap);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_AUDIOREAD_HIDDENAUDIOTRACK = %d\n",writerreaderdevs[count]->audioread_showhiddentrack);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_AUDIOREAD_USEPARANOIA = %d\n",writerreaderdevs[count]->audioread_useparanoia);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_AUDIOREAD_PARANOIARETRIES = %d\n",writerreaderdevs[count]->audioread_paranoiaretries);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_AUDIOREAD_PARANOIAREADAHEAD = %d\n",writerreaderdevs[count]->audioread_paranoiareadahead);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"WRITERREADER_AUDIOREAD_PARANOIAMINOVERLAP = %d\n",writerreaderdevs[count]->audioread_paranoiaminoverlap);
		fputs(tmp,fd);


		fputs("#\n",fd);
		count++;
	}

	loop = g_list_first(setupdata.image_dirs);
	while(loop) {
		strcpy(tmp2,(gchar *)loop->data);
		g_snprintf(tmp,MAXLINE,"IMAGE_DIRS = \"%s\"\n",convert_escape(tmp2));
		fputs(tmp,fd);
		loop = loop->next;
	}

	strcpy(tmp2,setupdata.dsp_device);
	g_snprintf(tmp,MAXLINE,"DSP_DEVICE = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	strcpy(tmp2,setupdata.mix_device);
	g_snprintf(tmp,MAXLINE,"MIX_DEVICE = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"NOTIFY_VIA = %d\n",setupdata.notify_via);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"NOTIFY_AT = %d\n",setupdata.notify_at);
	fputs(tmp,fd);

	strcpy(tmp2,setupdata.cddb_host);
	g_snprintf(tmp,MAXLINE,"CDDB_HOST = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"CDDB_PORT = %d\n",setupdata.cddb_port);
	fputs(tmp,fd);
	strcpy(tmp2,setupdata.cddb_proxy_host);
	g_snprintf(tmp,MAXLINE,"CDDB_PROXY_HOST = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"CDDB_PROXY_PORT = %d\n",setupdata.cddb_proxy_port);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"CDDB_USE_HTTP = %d\n",setupdata.cddb_use_http);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"CDDB_USE_PROXY = %d\n",setupdata.cddb_use_proxy);
	fputs(tmp,fd);

	strcpy(tmp2,setupdata.logfile);
	g_snprintf(tmp,MAXLINE,"LOGFILE = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"LOGLEVEL = %d\n",setupdata.loglevel);
	fputs(tmp,fd);

	strcpy(tmp2,setupdata.language);
	g_snprintf(tmp,MAXLINE,"LANGUAGE = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);

	g_snprintf(tmp,MAXLINE,"OPTION_TOOLTIPS = %d\n",setupdata.option_tooltips);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"OPTION_AUTORAISE = %d\n",setupdata.option_autoraise);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"OPTION_SAVEPOS = %d\n",setupdata.option_savepos);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"OPTION_PERSONIMAGE = %d\n",setupdata.option_personimage);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"OPTION_OVERWRITEWARN = %d\n",setupdata.option_overwritewarn);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"OPTION_AUTODELETE = %d\n",setupdata.option_autodelete);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"OPTION_TITLEPROGRESS = %d\n",setupdata.option_titleprogress);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"OPTION_DISPLAYCDTEXT = %d\n",setupdata.option_displaycdtext);
	fputs(tmp,fd);
	/* save geometry data? */
	if (setupdata.option_savepos) {
		g_snprintf(tmp,MAXLINE,"GEOMETRY_MAINWINDOW_X = %d\n", setupdata.mainwindow.x);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"GEOMETRY_MAINWINDOW_Y = %d\n", setupdata.mainwindow.y);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"GEOMETRY_MAINWINDOW_WIDTH = %d\n", setupdata.mainwindow.width);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"GEOMETRY_MAINWINDOW_HEIGHT = %d\n", setupdata.mainwindow.height);
		fputs(tmp,fd);

		g_snprintf(tmp,MAXLINE,"GEOMETRY_BURNWINDOW_X = %d\n", setupdata.burnwindow.x);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"GEOMETRY_BURNWINDOW_Y = %d\n", setupdata.burnwindow.y);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"GEOMETRY_BURNWINDOW_WIDTH = %d\n", setupdata.burnwindow.width);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"GEOMETRY_BURNWINDOW_HEIGHT = %d\n", setupdata.burnwindow.height);
		fputs(tmp,fd);
	}

	/* do save the following information only when root */
	if (isroot()) {

		g_snprintf(tmp,MAXLINE,"ROOT_USERS_ACCESS = %d\n",setupdata.root_users_access);
		fputs(tmp,fd);

		loop = g_list_first(setupdata.root_users_lists);
		while(loop) {
			strcpy(tmp2,(gchar *)loop->data);
			g_snprintf(tmp,MAXLINE,"ROOT_USERS_LISTS = \"%s\"\n",convert_escape(tmp2));
			fputs(tmp,fd);
			loop = loop->next;
		}

		g_snprintf(tmp,MAXLINE,"ROOT_HOSTS_ACCESS = %d\n",setupdata.root_hosts_access);
		fputs(tmp,fd);

		loop = g_list_first(setupdata.root_hosts_lists);
		while(loop) {
			strcpy(tmp2,(gchar *)loop->data);
			g_snprintf(tmp,MAXLINE,"ROOT_HOSTS_LISTS = \"%s\"\n",convert_escape(tmp2));
			fputs(tmp,fd);
			loop = loop->next;
		}

		g_snprintf(tmp,MAXLINE,"ROOT_OPTION_CHANGE_WRITER = %d\n",setupdata.root_option_change_writer);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"ROOT_OPTION_CHANGE_WRITEPARAM = %d\n",setupdata.root_option_change_writeparam);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"ROOT_OPTION_CHANGE_READER = %d\n",setupdata.root_option_change_reader);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"ROOT_OPTION_CHANGE_READPARAM = %d\n",setupdata.root_option_change_readparam);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"ROOT_OPTION_CHANGE_IMAGEDIRS = %d\n",setupdata.root_option_change_imagedirs);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"ROOT_OPTION_CHANGE_LOGOPTIONS = %d\n",setupdata.root_option_change_logoptions);
		fputs(tmp,fd);
	}

	if (fclose(fd) != 0) {
		/* error closing file */
		return 1;
	}

	return 0;
}


/*
 * Load the setup-configuration, return 0 if ok or 1 on error
 * when rootconf is set, load all from file, else only what we are allow
 */
gint load_setup_config(gchar *fname, gint rootconf) {
FILE *fd;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];
GList *tmp_image_dirs;
gint count, i, newadded;

	/* allocate memory if required */
	if (!writerreaderdevs) {
		writerreaderdevs = g_new0(writerreader_devices_t *, MAXDEVICES);
	}

	count = -1; 
	newadded = 0;
	tmp_image_dirs = NULL;

	/* prepare some data */
	if (rootconf) {
		free_glist(&setupdata.image_dirs);
	}
	if (rootconf) {
		free_glist(&setupdata.root_users_lists);
		free_glist(&setupdata.root_hosts_lists);
	}

	dodebug(1, "Opening config file %s for reading\n", fname);
	dolog(3, "Loading config file %s\n", fname);

	if ((fd = fopen(fname,"r")) == NULL) { 
		/* error opening file */
		return 1;
	}

	for (;;) {
		if (fgets(line,MAXLINE,fd) == NULL)
			break;
		dodebug(10,"config: %s", line);

		/* skip empty or hashed lines */
		strip_string(line);
		if (*line == '#' || *line == '\0') 
			continue;

                /* parse lines */
		if (parse_config_line(line,id,value)) {
                	g_error("syntax error in config-file\n");
		}	

		if (strcmp("VERSION",id) == 0) {
			strcpy(xcdroast_version_loaded,value); 
		}
		if (strcmp("PLATFORM",id) == 0) {
			;
		}

		if (rootconf || setupdata.root_option_change_writer) {
			if (strcmp("WRITER_DEVNR",id) == 0) {
				setupdata.writer_devnr = atoi(value);
			}
		}
		if (rootconf || setupdata.root_option_change_reader) {
			if (strcmp("READER_DEVNR",id) == 0) {
				setupdata.reader_devnr = atoi(value);
			}
		}

		/* marker for a new device - must be the first entry! */
		if (strcmp("WRITERREADER_DEVNR",id) == 0) {
			i = get_writerreaderdevs_index(atoi(value));

			if (i == -1) {
				/* new device */
				if (rootconf || (setupdata.root_option_change_writer &&
                			setupdata.root_option_change_reader)) {
				
						count = get_last_writerreaderdevs_index() +1;
						writerreaderdevs[count]=g_new0(writerreader_devices_t,1);
						writerreaderdevs[count]->devnr = atoi(value);
						newadded = 1;
				} else {
					/*
					 * no permission to add
					 * a new device -> ignore
					 */
					count = -1;
					newadded = 0;
				}
			} else {
				/* we seen such a device already */
				count = i;
				newadded = 0;
			}
		}	
			
		/* only load such data for newly allocated devices */
		if (newadded) {

			if (strcmp("WRITERREADER_DEVICESTR",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->devicestr = g_strdup(value);
				}	
			}
			if (strcmp("WRITERREADER_VENDOR",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					strncpy(writerreaderdevs[count]->vendor, value, 9);
				}	
			}
			if (strcmp("WRITERREADER_MODEL",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					strncpy(writerreaderdevs[count]->model, value, 17);
				}	
			}
			if (strcmp("WRITERREADER_REV",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					strncpy(writerreaderdevs[count]->rev, value, 5);
				}	
			}
			if (strcmp("WRITERREADER_READMAXSPEED",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->writer_readmaxspeed = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_CDRMAXSPEED",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->writer_cdrmaxspeed = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_DVDMAXSPEED",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->writer_dvdmaxspeed = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_WRITER_FLAGS",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->writer_flags = g_strdup(value);
				}	
			}
			if (strcmp("WRITERREADER_WRITER_MODES",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->writer_modes = g_strdup(value);
				}	
			}
			if (strcmp("WRITERREADER_IS_CDRWRITER",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->is_cdrwriter = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_IS_DVDREADER",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->is_dvdreader = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_IS_DVDWRITER",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->is_dvdwriter = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_SECTOR_SIZE",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->sector_size = atoi(value);
				}
			}
		}

		if (rootconf || newadded || setupdata.root_option_change_writeparam) {

			if (strcmp("WRITERREADER_DRVMODE",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->writer_drvmode = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_MODE",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->writer_mode = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_SPEED",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->writer_speed = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_FIFO",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->writer_fifo = atoi(value);
				}
			}
		}

		if (rootconf || newadded || setupdata.root_option_change_readparam) {

			if (strcmp("WRITERREADER_AUDIOREAD_SPEED",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->audioread_speed = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_AUDIOREAD_OVERLAP",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->audioread_overlap = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_AUDIOREAD_HIDDENAUDIOTRACK",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->audioread_showhiddentrack = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_AUDIOREAD_USEPARANOIA",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->audioread_useparanoia = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_AUDIOREAD_PARANOIARETRIES",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->audioread_paranoiaretries = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_AUDIOREAD_PARANOIAREADAHEAD",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->audioread_paranoiareadahead = atoi(value);
				}
			}
			if (strcmp("WRITERREADER_AUDIOREAD_PARANOIAMINOVERLAP",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->audioread_paranoiaminoverlap = atoi(value);
				}
			}
		}

		if (rootconf) {
			if (strcmp("IMAGE_DIRS",id) == 0) {
				setupdata.image_dirs = g_list_append(setupdata.image_dirs, g_strdup(value));	
			}
		} else {
			/* user config - load to tmp list */
			if (strcmp("IMAGE_DIRS",id) == 0) {
				tmp_image_dirs = g_list_append(tmp_image_dirs, g_strdup(value));	
			}
		}
		if (strcmp("DSP_DEVICE",id) == 0) {
			g_free(setupdata.dsp_device);
			setupdata.dsp_device = g_strdup(value);
		}
		if (strcmp("MIX_DEVICE",id) == 0) {
			g_free(setupdata.mix_device);
			setupdata.mix_device = g_strdup(value);
		}
		if (strcmp("NOTIFY_VIA",id) == 0) {
			setupdata.notify_via = atoi(value);
		}
		if (strcmp("NOTIFY_AT",id) == 0) {
			setupdata.notify_at = atoi(value);
		}

		if (strcmp("CDDB_HOST",id) == 0) {
			g_free(setupdata.cddb_host);
			setupdata.cddb_host = g_strdup(value);
		}
		if (strcmp("CDDB_PORT",id) == 0) {
			setupdata.cddb_port = atoi(value);
		}
		if (strcmp("CDDB_PROXY_HOST",id) == 0) {
			g_free(setupdata.cddb_proxy_host);
			setupdata.cddb_proxy_host = g_strdup(value);
		}
		if (strcmp("CDDB_PROXY_PORT",id) == 0) {
			setupdata.cddb_proxy_port = atoi(value);
		}
		if (strcmp("CDDB_USE_HTTP",id) == 0) {
			setupdata.cddb_use_http = atoi(value);
		}
		if (strcmp("CDDB_USE_PROXY",id) == 0) {
			setupdata.cddb_use_proxy = atoi(value);
		}

		if (rootconf || setupdata.root_option_change_logoptions) {
			if (strcmp("LOGFILE",id) == 0) {
				g_free(setupdata.logfile);
				setupdata.logfile = g_strdup(value);
			}
			if (strcmp("LOGLEVEL",id) == 0) {
				setupdata.loglevel = atoi(value);
			}
		}

		if (strcmp("LANGUAGE",id) == 0) {
			g_free(setupdata.language);
			setupdata.language = g_strdup(value);

			/* check if that value is an integer */
			if (atoi(value) != 0 || strcmp(value,"0") == 0) {
				/*
				 * it is, so set empty language
				 * to be backward compatible
				 */
				g_free(setupdata.language);
				setupdata.language = g_strdup("");
			}
		}

		if (strcmp("OPTION_TOOLTIPS",id) == 0) {
			setupdata.option_tooltips = atoi(value);
		}
		if (strcmp("OPTION_AUTORAISE",id) == 0) {
			setupdata.option_autoraise = atoi(value);
		}
		if (strcmp("OPTION_SAVEPOS",id) == 0) {
			setupdata.option_savepos = atoi(value);
		}
		if (strcmp("OPTION_PERSONIMAGE",id) == 0) {
			setupdata.option_personimage = atoi(value);
		}
		if (strcmp("OPTION_OVERWRITEWARN",id) == 0) {
			setupdata.option_overwritewarn = atoi(value);
		}
		if (strcmp("OPTION_AUTODELETE",id) == 0) {
			setupdata.option_autodelete = atoi(value);
		}
		if (strcmp("OPTION_TITLEPROGRESS",id) == 0) {
			setupdata.option_titleprogress = atoi(value);
		}
		if (strcmp("OPTION_DISPLAYCDTEXT",id) == 0) {
			setupdata.option_displaycdtext = atoi(value);
		}
		if (setupdata.option_savepos) {
			if (strcmp("GEOMETRY_MAINWINDOW_X",id) == 0) {
				setupdata.mainwindow.x = atoi(value);
			}
			if (strcmp("GEOMETRY_MAINWINDOW_Y",id) == 0) {
				setupdata.mainwindow.y = atoi(value);
			}
			if (strcmp("GEOMETRY_MAINWINDOW_WIDTH",id) == 0) {
				setupdata.mainwindow.width = atoi(value);
			}
			if (strcmp("GEOMETRY_MAINWINDOW_HEIGHT",id) == 0) {
				setupdata.mainwindow.height = atoi(value);
			}

			if (strcmp("GEOMETRY_BURNWINDOW_X",id) == 0) {
				setupdata.burnwindow.x = atoi(value);
			}
			if (strcmp("GEOMETRY_BURNWINDOW_Y",id) == 0) {
				setupdata.burnwindow.y = atoi(value);
			}
			if (strcmp("GEOMETRY_BURNWINDOW_WIDTH",id) == 0) {
				setupdata.burnwindow.width = atoi(value);
			}
			if (strcmp("GEOMETRY_BURNWINDOW_HEIGHT",id) == 0) {
				setupdata.burnwindow.height = atoi(value);
			}
		}

		/* do load the following information only when root */
		if (rootconf) {
			if (strcmp("ROOT_USERS_ACCESS",id) == 0) {
				setupdata.root_users_access = atoi(value);
			}
			if (strcmp("ROOT_USERS_LISTS",id) == 0) {
				setupdata.root_users_lists = g_list_append(setupdata.root_users_lists, g_strdup(value));	
			}
			if (strcmp("ROOT_HOSTS_ACCESS",id) == 0) {
				setupdata.root_hosts_access = atoi(value);
			}
			if (strcmp("ROOT_HOSTS_LISTS",id) == 0) {
				setupdata.root_hosts_lists = g_list_append(setupdata.root_hosts_lists, g_strdup(value));	
			}

			if (strcmp("ROOT_OPTION_CHANGE_WRITER",id) == 0) {
				setupdata.root_option_change_writer = atoi(value);
			}
			if (strcmp("ROOT_OPTION_CHANGE_WRITEPARAM",id) == 0) {
				setupdata.root_option_change_writeparam = atoi(value);
			}
			if (strcmp("ROOT_OPTION_CHANGE_READER",id) == 0) {
				setupdata.root_option_change_reader = atoi(value);
			}
			if (strcmp("ROOT_OPTION_CHANGE_READPARAM",id) == 0) {
				setupdata.root_option_change_readparam = atoi(value);
			}
			if (strcmp("ROOT_OPTION_CHANGE_IMAGEDIRS",id) == 0) {
				setupdata.root_option_change_imagedirs = atoi(value);
			}
			if (strcmp("ROOT_OPTION_CHANGE_LOGOPTIONS",id) == 0) {
				setupdata.root_option_change_logoptions = atoi(value);
			}
		}
	}
	/* now handle image dirs */
	if (!rootconf) {
		/* are we allowed to change image dirs? */
		if (setupdata.root_option_change_imagedirs) {
			/* had we configured some yet? */
			if (g_list_length(tmp_image_dirs) > 0) {
				/* use user one */
				copy_glist(&setupdata.image_dirs,
					tmp_image_dirs);
			}
		}
	}

	/* free temporary list */
	free_glist(&tmp_image_dirs);

	if (fclose(fd) != 0) {
		/* error closing file */
		return 1;
	}

	return 0;
}


/*
 * parse a detailed toc line
 */
static void parse_toc_line(gchar *line) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gchar old_cddb_title[MAXLINE];
gchar old_cdtext_title[MAXLINE];
gchar old_cdtext_artist[MAXLINE];
gchar *p1;
gint min,sec,frm;
gint ret;

	/* too many tracks? */
	if (tocnr >= MAXTRACKS) {
		g_error("over %d tracks in toc\n",MAXTRACKS);
	}

	strcpy(old_cddb_title,"");
	strcpy(old_cdtext_title,"");
	strcpy(old_cdtext_artist,"");

	/* allocate memory for new line (free first old memory)*/
	if (trackinfo[tocnr] != NULL) {
		g_free(trackinfo[tocnr]->volname);

		/* backup old cdtext */
		if (trackinfo[tocnr]->title) {
			strncpy(old_cdtext_title, trackinfo[tocnr]->title, MAXLINE);
		}
		g_free(trackinfo[tocnr]->title);
		/* backup old cdtext */
		if (trackinfo[tocnr]->artist) {
			strncpy(old_cdtext_artist, trackinfo[tocnr]->artist, MAXLINE);
		}
		g_free(trackinfo[tocnr]->artist);

		/* backup old cddb-title  */
		if (trackinfo[tocnr]->cddb_ttitle != NULL) {
			strncpy(old_cddb_title, trackinfo[tocnr]->cddb_ttitle, MAXLINE);
		}
		g_free(trackinfo[tocnr]->cddb_ttitle);
		g_free(trackinfo[tocnr]);
	}
	trackinfo[tocnr] = g_new0(track_info_t,1);

	/* when the cd is still the same as last time, restore cddb-title */
	if (cd_is_still_the_same == 1) {
		/* got a backuped title? */
		if (strcmp(old_cddb_title,"") != 0) {
			trackinfo[tocnr]->cddb_ttitle = g_strdup(old_cddb_title); 
		}
	}

	/* now we are prepared to fill structure */
	strcpy(tmp,line+1);
	p1 = strtok(tmp,":");
	if (p1 == NULL) {
		g_error("Unexpected output in cdda2wav toc-output\n");
	}
	trackinfo[tocnr]->track_nr = atoi(p1);
	if (trackinfo[tocnr]->track_nr == 0)
		cdinfo.nr_tracks++;
	trackinfo[tocnr]->isosize = -1;

	/* put rest of string into tmp2 and strip off leading spaces */
	p1 = strtok(NULL,"");
	strcpy(tmp2,p1);
	strip_string(tmp2);

	p1 = strtok(tmp2," ");
	trackinfo[tocnr]->start_sec = atoi(p1);

	p1 = strtok(NULL,"");
	if (p1 == NULL) {
		g_error("Unexpected output in cdda2wav toc-output\n");
	}
	strcpy(tmp,p1);
	strip_string(tmp);

	/* now get tracklength - convert to frames */
	p1 = strtok(tmp," ");
	sscanf(p1,"%d:%d.%d",&min,&sec,&frm);
	trackinfo[tocnr]->size = (min*60+sec)*75 + frm;

	/* tracktype */
	p1 = strtok(NULL," ");
	if (strncmp(p1,"data",4) == 0) {
		/* data-track */
		trackinfo[tocnr]->type = 0;

		p1 = strtok(NULL," ");
		if (strncmp(p1,"uninterrupted",13) == 0) {
			trackinfo[tocnr]->rec_type = 1;
		} else {
			trackinfo[tocnr]->rec_type = 0;
		}

		p1 = strtok(NULL," ");
		if (strncmp(p1,"copydenied",10) == 0) {
			trackinfo[tocnr]->copyperm = 0;
		} else {
			trackinfo[tocnr]->copyperm = 1;
		}	
		if (cd_is_still_the_same == 1) {
			/* use cached cd-text */
			trackinfo[tocnr]->title = g_strdup(old_cdtext_title);
			trackinfo[tocnr]->artist = g_strdup(old_cdtext_artist);
		}
	} else {
		/* audio-track */
		trackinfo[tocnr]->type = 1;
		
		p1 = strtok(NULL," ");
		if (strncmp(p1,"linear",6) == 0) {
			trackinfo[tocnr]->preemp = 0;
		} else {
			trackinfo[tocnr]->preemp = 1;
		}

		p1 = strtok(NULL," ");
		if (strncmp(p1,"copydenied",10) == 0) {
			trackinfo[tocnr]->copyperm = 0;
		} else {
			trackinfo[tocnr]->copyperm = 1;
		}	

		p1 = strtok(NULL," ");
		if (strncmp(p1,"stereo",6) == 0) {
			trackinfo[tocnr]->stereo = 1;
		} else {
			trackinfo[tocnr]->stereo = 0;
		}	

		p1 = strtok(NULL," ");
		p1 = strtok(NULL,"");
		if (p1) {
			strcpy(tmp2,p1);
			strip_string(tmp2);
			ret = decode_title_artist(tmp2,tmp,tmp3);
			if (ret != 0) {
				g_error("got unexpected artist/title\n");
			}

			if (cd_is_still_the_same == 1) {
				if (tmp[0] == '\0') {
					/* no new data, use cached */
					trackinfo[tocnr]->title = g_strdup(old_cdtext_title);
				}  else {
					trackinfo[tocnr]->title = g_strdup(tmp);
				}
				if (tmp3[0] == '\0') {
					/* no new data, use cached */
					trackinfo[tocnr]->artist = g_strdup(old_cdtext_artist);
				} else {
					trackinfo[tocnr]->artist = g_strdup(tmp3);
				}	
			} else {
				trackinfo[tocnr]->title = g_strdup(tmp);
				trackinfo[tocnr]->artist = g_strdup(tmp3);
			}
		} else {
			trackinfo[tocnr]->title = g_strdup("");
			trackinfo[tocnr]->artist = g_strdup("");
		}
	}	

	
	tocnr++;
}


/*
 * print memory-structure with toc-data (debug purposes)
 */
static void print_cdinfo() {
gint i;
gchar *p1, *p2, *p3, *p4;
gchar tmp[] = "(NULL)";

	dodebug(2,"------ cdinfo-structure -----\n");
	dodebug(2,"nr_tracks: %d\n",cdinfo.nr_tracks);
	dodebug(2,"total_size: %d\n",cdinfo.total_size);
	dodebug(2,"cddb_discid: %s\n",cdinfo.cddb_discid);
	dodebug(2,"have_cdtext: %d, have_cdextra: %d\n",cdinfo.have_cdtext,
		 cdinfo.have_cdextra);
	if (cdinfo.title != NULL && cdinfo.artist != NULL) {
		dodebug(2,"title/artist: %s/%s\n",cdinfo.title,cdinfo.artist);
	} else {
		dodebug(2,"title/artist: (NULL)/(NULL)\n");
	}
	if (cdinfo.cddb_dtitle != NULL) {
		dodebug(2,"cddb_dtitle: %s\n",cdinfo.cddb_dtitle);
	} else {
		dodebug(2,"cddb_dtitle: (NULL)\n");
	}
	dodebug(2,"leadout: %d\n", cdinfo.leadout);

	for (i = 0; i < cdinfo.nr_tracks; i++) {

		dodebug(2,"track: %d start: %d size: %d\n",trackinfo[i]->track_nr,
			trackinfo[i]->start_sec, trackinfo[i]->size);
		dodebug(2,"       type: %d, rec_type: %d, preemp: %d, copyperm: %d, stereo: %d\n", 
			trackinfo[i]->type, trackinfo[i]->rec_type,
			trackinfo[i]->preemp, trackinfo[i]->copyperm,
			trackinfo[i]->stereo);
		/* check if we have any null-pointers lying around */
		p1 = trackinfo[i]->title;
		p2 = trackinfo[i]->artist;
		p3 = trackinfo[i]->cddb_ttitle;
		p4 = trackinfo[i]->volname;
		if (p1 == NULL) p1 = tmp;
		if (p2 == NULL) p2 = tmp;
		if (p3 == NULL) p3 = tmp;
		if (p4 == NULL) p4 = tmp;

		dodebug(2,"       title/artist: %s/%s cddb_title: %s volname: %s\n", 
			p1, p2, p3, p4);
	}
}


/*
 * interpret line for line the output of cdda2wav
 */
static gint parse_toc(gchar *line) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gchar *p,*p1;
gint min,sec,frm;
gint ret;
gint mmaperror;

	mmaperror = 0;

	/* initial state */
	if (tocstate == 0) {
		/* mmap error? */
		if (strstr(line,"annot get mmap for")) {
			mmaperror = 1;
		}

		/* first look for "Tracks" line */
		if (strncmp(line,"Tracks:",7) == 0) {
			strcpy(tmp,line+7);
			p1 = strtok(tmp," ");
			strcpy(tmp2,p1);
			cdinfo.nr_tracks = atoi(tmp2);
			p1 = strtok(NULL,"");
			strcpy(tmp2,p1);
			sscanf(tmp2,"%d:%d.%d",&min,&sec,&frm);			
			cdinfo.total_size = (min*60+sec)*75 + frm;
		}
		if (strncmp(line,"CDINDEX",7) == 0) {
			/* we don't support cdindex right now */
			;
		}
		if (strncmp(line,"CDDB",4) == 0) {
			strcpy(tmp,line+15);
			strip_string(tmp);
			strcpy(cdinfo.cddb_discid,tmp);
			/*
			 * check if the CD we have in the drive
			 * is the same as the last one we got
			 */
			if (strcmp(cdinfo_cddb_title_bak, cdinfo.cddb_discid) == 0) {
				cd_is_still_the_same = 1;
			}
	
			/* finished with state 0 */
			tocstate = 1;
		}

		/* get next line from output */
		return mmaperror;
	}

	/* cd-text state */
	if (tocstate == 1) {
		if (strncmp(line,"CD-Text: detected",17) == 0) {
			cdinfo.have_cdtext = 1;
		}
		if (strncmp(line,"CD-Text: not detected",21) == 0) {
			cdinfo.have_cdtext = 0;
		}

		/* ignore any cd-text-output for now */
		;

		if (strncmp(line,"CD-Extra: ",10) == 0) {
			/* see this as endmarker of cd-text section */
			tocstate = 2;
		}
		/*
		 * don't return here because
		 * we may need the current line
		 */
	}

	/* cd-extra state */
	if (tocstate == 2) {
		if (strncmp(line,"CD-Extra: detected",18) == 0) {
			cdinfo.have_cdextra = 1;
		}
		if (strncmp(line,"CD-Extra: not detected",22) == 0) {
			cdinfo.have_cdextra = 0;
		}

		/* ignore any cd-extra-output for now */
		;
		
		if (strstr(line,"Album title:") != NULL) {
			/* see this as endmarker of cd-extra section */
			tocstate = 3;
		}
		/*
		 * don't return here because
		 * we may need the current line
		 */
	}
	
	/* toc-listing mode */
	if (tocstate == 3) {
		if ((p = strstr(line,"Album title:")) != NULL) {
			strcpy(tmp,p+13);
			strip_string(tmp);
			ret = decode_title_artist(tmp,tmp2,tmp3);
			if (ret != 0) {
				g_error("got unexpected artist/title\n");
			}	
			g_free(cdinfo.title);

			cdinfo.title = g_strdup(tmp2);
			g_free(cdinfo.artist);
			cdinfo.artist = g_strdup(tmp3);
		}

		/* now the most complex part: the toc-list itself */
		if (line[0] == 'T' && line[3] == ':') {
			parse_toc_line(line);
		}	

		if (strncmp(line,"Leadout:",8) == 0) {
			strcpy(tmp,line+8);
			strip_string(tmp);
			cdinfo.leadout = atoi(tmp);
		}
	}

	return 0;
}


/*
 * called when reading the TOC of ATAPI cdroms takes too long
 */
static gint getcdtoc_atapi_timeout_func(gpointer data) {

	getcdtoc_atapi_timeout_dialog = show_dialog_wait(ICO_INFO,
		_("Checking drive for a medium.\n\nUsing scsi-emulation for that drive would speed up\nthis considerably. Please note that inserting a\nmedium would also reduce the wait time."));

	return (FALSE);
}


static gboolean getcdtoc_out(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	if (cond & G_IO_IN) {
        /* read output of getcdtoc */
	    read_io_channel(source, line, MAXLINE);

	    dodebug(10,"readtoc: %s\n", line);

	    count_mmaperror += parse_toc(line);
    	}

        /* getcdtoc finished */
	if (cond == G_IO_HUP) {

                /* tell our caller that we are done here */
                read_done = get_wait_ret(-1);
                return (FALSE);
        }
	return (TRUE);
}


/*
 * call cdda2wav -J to get the cd-toc
 * add ATAPI long-delay warning window
 */
void get_cd_toc(gint devnr) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp8[MAXLINE];
gint ret, dvdsize, read_out;
gchar old_cdtext_title[MAXLINE];
gchar old_cdtext_artist[MAXLINE];
gint getcdtoc_timeout;
GIOChannel *gio;

	/* get bus,id,lun string */
	if (convert_devnr2busid(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	getcdtoc_atapi_timeout_dialog = NULL;
	getcdtoc_timeout = 0;

	/* ATAPI device? (only on linux) */
	if (strstr(tmp, "ATAPI")) {
		/* if it does take longer than 5 seconds, display warning */
		getcdtoc_timeout = g_timeout_add(5000,
			getcdtoc_atapi_timeout_func, NULL);
	}
	
	/* check if disc is loaded */
	if (!check_medium_loaded(devnr)) {
		/* not loaded */
		cdinfo.nr_tracks = -1;

		if (getcdtoc_timeout) {
			/* remove timeout, if it did not kick in yet */
			g_source_remove(getcdtoc_timeout);
		}	
		if (getcdtoc_atapi_timeout_dialog) {
			/* and remove the dialog wait window */
			show_dialog_wait_remove(getcdtoc_atapi_timeout_dialog);
		}	

		return;
	}

	cdinfo.have_cdtext = -1;
	cdinfo.have_cdextra = -1;
	cdinfo.nr_tracks = -1;
	
	read_done = 999;
	tocstate = 0;
	tocnr = 0;
	cd_is_still_the_same = 0;
	count_mmaperror = 0;


	/*
	 * backup cddb-value to check if the new CD
	 * is the same as the last one we read
	 */
	strncpy(cdinfo_cddb_title_bak, cdinfo.cddb_discid, MAXLINE);

	/* backup current cd-text album info */
	if (cdinfo.title) {
		strncpy(old_cdtext_title, cdinfo.title, MAXLINE);
	} else {
		strcpy(old_cdtext_title, "");
	}
	if (cdinfo.artist) {
		strncpy(old_cdtext_artist, cdinfo.artist, MAXLINE);
	} else {
		strcpy(old_cdtext_artist, "");
	}

	/* have to enable -no-hidden-track? */
	if (get_cur_audioread_showhiddentrack()) {
		strcpy(tmp8,"");
	} else {
		strcpy(tmp8,"-no-hidden-track");
	}

	/* build command line */
	get_wrap_path("CDDA2WAV", tmp2);
	g_snprintf(line,MAXLINE,
		   "%s -D \"%s\" %s -Q -g -H -J -v toc,summary,sectors,titles",
		   tmp2,tmp,tmp8);

	dodebug(1,"spawning: %s\n", line);

        readcdda_pid = full_dpl_pipe3(NULL,NULL,&read_out,line,0);
        readcdda_pid2 = -1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                getcdtoc_out, NULL);

        /* now wait until toc is read */
        while (read_done == 999) {
                wait_and_process_events();
        }

	g_io_channel_unref(gio);
        close(read_out);

	ret = read_done;

	/* error while reading tracks? */
	if (ret != 0 && cdinfo.nr_tracks != tocnr) {
		cdinfo.nr_tracks = -2;
	}

	/* cd loaded but not readable = empty recordable? */
	if (ret != 0 && cdinfo.nr_tracks < 1 ) {
		cdinfo.nr_tracks = -2;
	}

	/* if cd changed, clear out cddb_dtitle */
	if (cd_is_still_the_same == 0) {
		g_free(cdinfo.cddb_dtitle);
		cdinfo.cddb_dtitle = NULL;
	} else {
		/* cd stayed the same, restore album cd-text */
		if (cdinfo.title[0] == '\0' && old_cdtext_title[0] != '\0') {
			g_free(cdinfo.title);
			cdinfo.title = g_strdup(old_cdtext_title);
		}
		if (cdinfo.artist[0] == '\0' && old_cdtext_artist[0] != '\0') {
			g_free(cdinfo.artist);
			cdinfo.artist = g_strdup(old_cdtext_artist);
		}
	}

	/*
	 * now a workaround for a broken cdda2wav
	 * when it comes to read the toc of a DVD
	 * -> use cdrecord -toc instead
	 */
	if (cdinfo.nr_tracks == 1 && (cdinfo.leadout == 1151850 || cdinfo.leadout == 1151849 || cdinfo.leadout == 449849)) {
		/* this is slow, so only use it in that rare case */	
		dvdsize = get_cdrecord_toc(devnr);
		if (dvdsize > 0) {
			/*
			 * overwrite faulty values by the real ones
			 * 1152000 is 256:00.00 which is not right
			 * 449999 is  100:00.00 -1 frame
			 */
			cdinfo.leadout = dvdsize;
			trackinfo[0]->size = dvdsize;
			cdinfo.total_size = dvdsize + 150;
		}
	}

	/* have we hit the mmap error? */
	if (count_mmaperror > 0) {
                ret = show_dialog(ICO_WARN, _("Warning: The cdrtools binaries you have installed are\nnot compatible with your system. You will NOT be\nable to read or write CDs. Please read the FAQ\non www.xcdroast.org for more information."), T_ANYWAY, _("Exit"), NULL, 1);
                if (ret == 1) 
                        gtk_exit(1);
	}

	if (getcdtoc_timeout) {
		/* remove timeout, if it did not kick in yet */
		g_source_remove(getcdtoc_timeout);
	}	
	if (getcdtoc_atapi_timeout_dialog) {
		/* and remove the dialog wait window */
		show_dialog_wait_remove(getcdtoc_atapi_timeout_dialog);
	}	

	if (debug) print_cdinfo();

}


/*
 * start subprocess and reroute stdin and stdout
 * use /bin/sh to spawn subprocess
 */
pid_t full_dpl_pipe_shell(gint *out, gint *in, gchar *cmd) {
gint fd1[2], fd2[2];
pid_t pid;

	if (pipe(fd1) <0 || pipe(fd2) <0) {
		g_error("pipe error\n");
	}

	if (( pid = fork()) < 0) {
		g_error("fork error\n");
	} else 
	if (pid > 0) {
		/* parent */
		close(fd1[0]);
		close(fd2[1]);

		/* return new stdout/stdin of child */
		if (in != NULL) 
			*in = fd1[1];
		else
			close(fd1[1]);

		if (out != NULL)
			*out = fd2[0];
		else
			close(fd2[0]);

		dodebug(11, "Created child pid: %d\n", pid); 
	} else {

		/* child */
		close(fd1[1]);
		close(fd2[0]);

		/* reroute stdin from child */
		if (fd1[0] != STDIN_FILENO) {
			if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO) {
				g_error("dup2 error on stdin\n");
			}
			close(fd1[0]);
		}
		/* reroute stdout from child */
		if (fd2[1] != STDOUT_FILENO) {
			if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO) {
				g_error("dup2 error on stdout\n");
			}
			close(fd2[1]);
		}

		g_setenv("LC_ALL", "C", TRUE);

		/* startup child */
		if (execl("/bin/sh", "sh", "-c", cmd ,(void *)NULL) < 0) {
			g_error("execl error\n");
		}
 
	}

	return(pid);
}


/*
 * rebuild a cmdline-string into a list of arguments..handle quotes and
 * escaped chars nicely
 */
static void rebuild_cmdline(char **arglist, gchar *cmd, gchar *callpath) {
gchar tmp[MAXLINE*10];
gchar *p;
gint n, count, start, in_quotes;
gchar oldc, c;

	n = 0;
	count = 0;
	start = 0;
	in_quotes = 0;
	oldc = '\0';
	for (n = 0; n <= strlen(cmd); n++) {
		c = cmd[n];
		/*
		 * quotes found?
		 * search for closing quote
		 * ignore escaped quotes
		 */
		if (c == '\"' && oldc != '\\') {
			if (in_quotes == 0) {
				in_quotes = 1;
			} else {
				/* end-quote found */
				in_quotes = 0;
			}
		}
		oldc = c;

		/* space is delimitor between args */
		if (in_quotes == 0 && (c == ' ' || c == '\0')) {
			strncpy(tmp,cmd+start,n-start);
			tmp[n-start] = '\0';

			/* skip empty args */
			if (n-start == 0) {
				continue;
			}
			strip_string(tmp);
			/*
			 * tmp does contain now our substr
			 * remove surrounding quotes if any
			 * and remove any escaped chars
			 */
			if (tmp[0] == '\"') {
				p = tmp + 1;
			} else {
				p = tmp;
			}
 
			if (tmp[strlen(tmp)-1] == '\"') {
				tmp[strlen(tmp)-1] = '\0';
			}
			escape_parse(p); 

			/* empty unquoted string? skip */
			if (p == tmp && strcmp(p,"") == 0) {
				continue;
			}
	
			arglist[count] = g_strdup(p);
			count++;
			start = n+1;
		}	

		if (count >= MAXPIPEARGS) {
			g_error("Error: More than %d cmd arguments given\n",MAXPIPEARGS);
		}
	}

	/* now remove path from first argument */
	strncpy(callpath,arglist[0], MAXLINE);
	strncpy(tmp,arglist[0], MAXLINE*10);
	p = rindex(tmp,'/');
	if (p != NULL) {
		g_free(arglist[0]);
		arglist[0] = g_strdup(p+1);
	}	

	/* now arglist is finished */

	/* output generated argument-list */
	if (debug) {
		dodebug(11,"----- created argument array for execv -----\n");
		count = 0;
		dodebug(11,":%s:\n",callpath);
		while(arglist[count]) {
			dodebug(11,":%s:\n",arglist[count]);
			count++;
		}
	}
}



/*
 * create a bidirectional pipe
 */
static gint my_s_pipe(gint fd[2], gint use_socketpair) {

	if (use_socketpair == 1) {
#if defined(HAVE_SOCKETPAIR) && HAVE_SOCKETPAIR == 1
		return (socketpair(AF_UNIX, SOCK_STREAM, 0, fd));
#else
		/* socketpair not available - fallback */
		return (pipe(fd));
#endif
	} else {
		return (pipe(fd));
	}
}


/*
 * start subprocess and reroute stdin, stderr and stdout
 * does NOT use the shell as spawner
 */
static pid_t full_dpl_pipe3(gint *out, gint *in, gint *err, gchar *cmd, 
	gint use_socketpair) { 
gint fd1[2], fd2[2], fd3[2];
gchar **arglist;
gchar callpath[MAXLINE];
pid_t pid;
gint count;

	/* rebuild cmdline to array of args for execv */
	arglist = g_new0(gchar *, MAXPIPEARGS);
	rebuild_cmdline(arglist, cmd, callpath);

	/* do the pipe-stuff */
	if (my_s_pipe(fd1, use_socketpair) < 0 || 
	    my_s_pipe(fd2, use_socketpair) < 0 || 
	    my_s_pipe(fd3, use_socketpair) < 0) {
		g_error("pipe error\n");
	}

	if (( pid = fork()) < 0) {
		g_error("fork error\n");
	} else 
	if (pid > 0) {
		/* parent */
		close(fd1[0]);
		close(fd2[1]);
		close(fd3[1]);

		/* return new stdout/stdin/stderr of child */
		if (in != NULL) 
			*in = fd1[1];
		else
			close(fd1[1]);

		if (out != NULL)
			*out = fd2[0];
		else
			close(fd2[0]);

		if (err != NULL)
			*err = fd3[0];
		else 
			close(fd3[0]);

		dodebug(11, "Created child pid: %d\n", pid); 
	} else {

		/* child */
		close(fd1[1]);
		close(fd2[0]);
		close(fd3[0]);

		/* reroute stdin from child */
		if (fd1[0] != STDIN_FILENO) {
			if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO) {
				g_error("dup2 error on stdin\n");
			}
			close(fd1[0]);
		}
		/* reroute stdout from child */
		if (fd2[1] != STDOUT_FILENO) {
			if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO) {
				g_error("dup2 error on stdout\n");
			}
			close(fd2[1]);
		}
		/* reroute stderr from child */
		if (fd3[1] != STDERR_FILENO) {
			if (dup2(fd3[1], STDERR_FILENO) != STDERR_FILENO) {
				g_error("dup2 error on stderr\n");
			}
			close(fd3[1]);
		}

		g_setenv("LC_ALL", "C", TRUE);

		/* startup child */
		if (execv(callpath,arglist) < 0) {
			g_error("execv error\n");
		}
 
	}

	/* free arglist */
	count = 0;
	while(arglist[count]) {
		g_free(arglist[count]);
		count++;
	}
	g_free(arglist);

	return(pid);
}


/*
 * start two subprocesses and connect stdout of the first with stdin
 * of the second (proc1 | proc2) and reroute stdin and stderr of the
 * first and stdout and stderr of the second
 * returns pidnr1 and pidnr2
 */
static void full_dpl_pipe4(pid_t *pidnr1, pid_t *pidnr2, gint *in1, gint *err1, 
		    gchar *cmd, gint *out2, gint *err2, gchar *cmd2,
		    gint use_socketpair) {
gint fd1[2], fd2[2], fd3[2], fd4[2], pipefd[2];
gchar **arglist;
gchar **arglist2;
gchar callpath[MAXLINE];
gchar callpath2[MAXLINE];
pid_t pid, pid2;
gint count;

	/* rebuild cmdline to array of args for execv */
	arglist = g_new0(gchar *, MAXPIPEARGS);
	arglist2 = g_new0(gchar *, MAXPIPEARGS);
	rebuild_cmdline(arglist, cmd, callpath);
	rebuild_cmdline(arglist2, cmd2, callpath2);

	/* do the pipe-stuff */
	if (my_s_pipe(fd1, use_socketpair) < 0 || 
	    my_s_pipe(fd2, use_socketpair) < 0 || 
	    my_s_pipe(fd3, use_socketpair) < 0 ||
	    my_s_pipe(fd4, use_socketpair) < 0 || 
	    my_s_pipe(pipefd, use_socketpair) < 0) {
		g_error("pipe error\n");
	}

	/* fork for first process */
	if (( pid = fork()) < 0) {
		g_error("fork error\n");
	} else 
	if (pid > 0) {
		/* parent */
		close(fd1[0]);
		close(fd2[1]);
		close(pipefd[1]);

		/* return new fds of child */
		if (in1 != NULL) 
			*in1 = fd1[1];
		else
			close(fd1[1]);

		if (err1 != NULL)
			*err1 = fd2[0];
		else
			close(fd2[0]);

		dodebug(11, "Created child pid: %d\n", pid); 
	} else {

		/* child */
		close(fd1[1]); 
		close(fd2[0]);
		close(pipefd[0]);

		/* reroute stdin from child */
		if (fd1[0] != STDIN_FILENO) {
			if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO) {
				g_error("dup2 error on stdin\n");
			}
			close(fd1[0]);
		}

		/* reroute stderr from child */
		if (fd2[1] != STDERR_FILENO) {
			if (dup2(fd2[1], STDERR_FILENO) != STDERR_FILENO) {
				g_error("dup2 error on stderr\n");
			}
			close(fd2[1]);
		}

		/* reroute stdout from child to second child */
		if (pipefd[1] != STDOUT_FILENO) {
			if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
				g_error("dup2 error on stdout\n");
			}
			close(pipefd[1]); 
		} 

		g_setenv("LC_ALL", "C", TRUE);

		/* startup first child */
		if (execv(callpath,arglist) < 0) {
			g_error("execv error\n");
		}
	}


	/* fork for second process */
	if (( pid2 = fork()) < 0) {
		g_error("fork error\n");
	} else 
	if (pid2 > 0) {
		/* parent */
		close(fd3[1]);
		close(fd4[1]);
		close(pipefd[0]);

		/* return new fds of child */
		if (out2 != NULL)
			*out2 = fd3[0];
		else
			close(fd3[0]);

		if (err2 != NULL)
			*err2 = fd4[0];
		else 
			close(fd4[0]);

		dodebug(11, "Created second child pid: %d\n", pid2); 
	} else {

		/* child */
		close(fd3[0]);
		close(fd4[0]);
		close(pipefd[1]);

		/* reroute stdin from first child */
		if (pipefd[0] != STDIN_FILENO) {
			if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
				g_error("dup2 error on stdin\n");
			}
			close(pipefd[0]); 
		}

		/* reroute stderr from child */
		if (fd4[1] != STDERR_FILENO) {
			if (dup2(fd4[1], STDERR_FILENO) != STDERR_FILENO) {
				g_error("dup2 error on stdout\n");
			}
			close(fd4[1]);
		}

		/* reroute stdout from child to second child */
		if (fd3[1] != STDOUT_FILENO) {
			if (dup2(fd3[1], STDOUT_FILENO) != STDOUT_FILENO) {
				g_error("dup2 error on stdout\n");
			}
			close(fd3[1]);
		}

		g_setenv("LC_ALL", "C", TRUE);

		/* startup second child */
		if (execv(callpath2,arglist2) < 0) {
			g_error("execv error\n");
		}
	}

	/* free arglists */
	count = 0;
	while(arglist[count]) {
		g_free(arglist[count]);
		count++;
	}
	g_free(arglist);
	count = 0;
	while(arglist2[count]) {
		g_free(arglist2[count]);
		count++;
	}
	g_free(arglist2);

	/* return pids by call by reference */
	*pidnr1 = pid;
	*pidnr2 = pid2;
}


/*
 * get the master-volume
 * return val 0..100 or -1 or error
 */
gint query_mixer() {
#if !(defined(linux))
gint mix;
#endif
#if defined(linux)
gint err = 0;
snd_mixer_t *handle = NULL;
snd_mixer_elem_t *elem;
snd_mixer_selem_id_t *sid;
long val, pmin, pmax;
#endif
#if defined(__FreeBSD__)
gint val;
#endif
#if defined(sun) || defined(__OpenBSD__)
audio_info_t ainfo;
#endif
#ifdef hpux
# ifndef hpux_alib
struct audio_gain again;
# endif
#endif

	if (strcmp(setupdata.mix_device,"") == 0) {
		/* no mixer found - ignore */
		return -1;
	}

#if defined(linux)
	dodebug(10,"querying mixer '%s'\n", setupdata.mix_device);
	snd_mixer_selem_id_alloca(&sid);

    if ((err = snd_mixer_open(&handle, 0)) < 0) {
    	g_warning("Mixer %s open error: %s\n", setupdata.mix_device, snd_strerror(err));
		return -1;
    }

	if ((err = snd_mixer_attach(handle, setupdata.mix_device)) < 0) {
		g_warning("Mixer attach %s error: %s", setupdata.mix_device, snd_strerror(err));	
		snd_mixer_close(handle);
		return -1;
	}

	if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
		g_warning("Mixer register error: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return -1;
	}

	if ((err = snd_mixer_load(handle)) < 0) {
		g_warning("Mixer %s load error: %s", setupdata.mix_device, snd_strerror(err));
		snd_mixer_close(handle);
		return -1;
	}

	/* use first found mixer control */
    elem = snd_mixer_first_elem(handle);
	if (!elem) {
    	g_warning("Unable to find a simple control for this device");
        snd_mixer_close(handle);
        handle = NULL;
		return -1;
    } else {
        snd_mixer_selem_get_id(elem, sid);
        dodebug(10, "Using simple mixer control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
    }   

	if ((err = snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax)) < 0) {
		g_warning("Failed to get playback volume range: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return -1;
	}

	if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &val)) < 0) {
		g_warning("Failed to get playback volume: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return -1;
	}

	snd_mixer_close(handle);

	return (gint)(100*val/(pmax-pmin));	

#endif

#if defined(__FreeBSD__)

	dodebug(10,"querying mixer %s\n", setupdata.mix_device);
	mix = open(setupdata.mix_device, O_RDWR);
	if (mix < 0) {
		g_warning("Can't open mixer-device %s\n", setupdata.mix_device);
		return -1;
	}

	if (ioctl(mix,MIXER_READ(0),&val) == -1) {
		g_warning("Error reading mixer\n");
		return -1;
	}	

	close(mix);

	/* average val of both channels */
	return ((val & 0x7f) + ((val >> 8) & 0x7f))/2;

#endif
#if defined(sun) || defined(__OpenBSD__)

	dodebug(10,"querying mixer %s\n", setupdata.mix_device);
	mix = open(setupdata.mix_device, O_RDONLY);
	if (mix < 0) {
		g_warning("Can't open mixer-device %s\n", setupdata.mix_device);
		return -1;
	}

	if (ioctl(mix, AUDIO_GETINFO, &ainfo) == -1) {
		g_warning("Error reading mixer\n");
		return -1;
	}

	close(mix);

	return ((gint) ((gfloat) ainfo.play.gain / 2.55));

#endif
#ifdef aix
	/*
	 * Due to we are using the raw aix audio api,
	 * we are not able to query the gain settings :-(
	 * set the gain to the max. value and return 100
	 */
	memset ( & achange, '\0', sizeof (achange));
	memset ( & acontrol, '\0', sizeof (acontrol));

	/* Initialise with the max gain =2147418112      */
	achange.balance        = 0x3fff0000;
	achange.balance_delay  = 0;
	achange.volume         = 2147418112;
	achange.volume_delay   = 0;
	achange.input          = AUDIO_IGNORE;
	achange.output         = AUDIO_IGNORE;
	achange.output         = AUDIO_IGNORE;
	achange.treble         = AUDIO_IGNORE;
	achange.bass           = AUDIO_IGNORE;
	achange.pitch          = AUDIO_IGNORE;
	achange.monitor        = AUDIO_IGNORE;
	achange.dev_info       = (char *) NULL;
	acontrol.ioctl_request = AUDIO_CHANGE;
	acontrol.position      = 0;
	acontrol.request_info  = (char *) & achange;

	if ((mix = open(setupdata.mix_device, O_RDONLY)) == -1) {
		g_warning("Can't open mixer-device %s\n", setupdata.mix_device);
		return -1;
        }
	if (ioctl (mix, AUDIO_CONTROL, & acontrol) == -1) {
		g_warning("Error reading mixer\n");
		return -1;
	}
	close(mix);

	return (100);
#endif
#ifdef hpux
# ifndef hpux_alib
	dodebug(10,"querying mixer %s\n", setupdata.mix_device);
	if ((mix = open(setupdata.mix_device, O_RDONLY)) == -1) {
		g_warning("Can't open mixer-device %s\n", setupdata.mix_device);
		return -1;
	}
	if (ioctl(mix, AUDIO_GET_GAINS, &again) == -1) {
		g_warning("Error reading mixer\n");
		return -1;
	}
	close(mix);
	return ((gint) ((gfloat) (again.transmit_gain - AUDIO_OFF_GAIN)
			/ (gfloat) (AUDIO_MAX_GAIN - AUDIO_OFF_GAIN) * 100.0));
# else
	/*
	 * The audio transmission gains are set by some
	 * environment variables or a fancy program
	 * like the AudioCP
	 */
	return (0);
# endif
#endif

	return 0;
}


/*
 * set the master-volume
 * returns 0 on ok, -1 on error
 */
gint set_mixer(gint val) {
#if !(defined(linux))
gint mix;
#endif
#if defined(linux)
gint err = 0;
snd_mixer_t *handle = NULL;
snd_mixer_elem_t *elem;
snd_mixer_selem_id_t *sid;
long sval, pmin, pmax;
#endif
#if defined(sun) || defined(__OpenBSD__)
audio_info_t ainfo;
#endif
#ifdef aix
audio_change   achange;
audio_control  acontrol;
#endif
#ifdef hpux
# ifndef hpux_alib
struct audio_gain again;
# endif
#endif

	if (strcmp(setupdata.mix_device,"") == 0) {
		/* no mixer found - ignore */
		return -1;
	}

#if defined(linux)
	dodebug(10,"setting mixer '%s' to %d\n", setupdata.mix_device, val);
	snd_mixer_selem_id_alloca(&sid);

    if ((err = snd_mixer_open(&handle, 0)) < 0) {
    	g_warning("Mixer %s open error: %s\n", setupdata.mix_device, snd_strerror(err));
		return -1;
    }

	if ((err = snd_mixer_attach(handle, setupdata.mix_device)) < 0) {
		g_warning("Mixer attach %s error: %s", setupdata.mix_device, snd_strerror(err));	
		snd_mixer_close(handle);
		return -1;
	}

	if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
		g_warning("Mixer register error: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return -1;
	}

	if ((err = snd_mixer_load(handle)) < 0) {
		g_warning("Mixer %s load error: %s", setupdata.mix_device, snd_strerror(err));
		snd_mixer_close(handle);
		return -1;
	}

	/* use first found mixer control */
    elem = snd_mixer_first_elem(handle);
	if (!elem) {
    	g_warning("Unable to find a simple control for this device");
        snd_mixer_close(handle);
        handle = NULL;
		return -1;
    } else {
        snd_mixer_selem_get_id(elem, sid);
    }   

	if ((err = snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax)) < 0) {
		g_warning("Failed to get playback volume range: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return -1;
	}

	sval = val*(pmax-pmin)/100;
	if ((err = snd_mixer_selem_set_playback_volume_all(elem, sval)) < 0) {
		g_warning("Failed to set playback volume: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return -1;
	}

	snd_mixer_close(handle);

	return 0;
#endif

#if defined(__FreeBSD__)

	dodebug(10,"setting mixer %s to %d\n", setupdata.mix_device, val);
	mix = open(setupdata.mix_device, O_RDWR);
	if (mix < 0) {
		g_warning("Can't open mixer-device\n");
		return -1;
	}

	val |= val << 8;
	if (ioctl(mix, MIXER_WRITE(0), &val) == -1) {
		g_warning("Error writing mixer\n");
		return -1;
	}

	close(mix);

#endif 
#if defined(sun) || defined(__OpenBSD__)

	dodebug(10,"setting mixer %s to %d\n", setupdata.mix_device, val);
	mix = open(setupdata.mix_device, O_WRONLY);
	if (mix < 0) {
		g_warning("Can't open mixer-device %s\n", setupdata.mix_device);
		return -1;
	}

	AUDIO_INITINFO(&ainfo);
	ainfo.play.gain = (gint) (2.55 * (gfloat) val);

	if (ioctl(mix, AUDIO_SETINFO, &ainfo) == -1) {
		g_warning("Error writing mixer\n");
		return -1;
	}

	close(mix);
#endif
#ifdef aix
       memset ( & achange, '\0', sizeof (achange));
       memset ( & acontrol, '\0', sizeof (acontrol));

       /* Initialise with the max gain =2147418112      */
       achange.balance        = AUDIO_IGNORE;
       achange.balance_delay  = AUDIO_IGNORE;
       achange.volume         = (long) (21474181.12 * (gfloat) val);
       achange.volume_delay   = 0;
       achange.input          = AUDIO_IGNORE;
       achange.output         = AUDIO_IGNORE;
       achange.output         = AUDIO_IGNORE;
       achange.treble         = AUDIO_IGNORE;
       achange.bass           = AUDIO_IGNORE;
       achange.pitch          = AUDIO_IGNORE;
       achange.monitor        = AUDIO_IGNORE;
       achange.dev_info       = (char *) NULL;
       acontrol.ioctl_request = AUDIO_CHANGE;
       acontrol.position      = 0;
       acontrol.request_info  = (char *) & achange;

       if ((mix = open(setupdata.mix_device, O_RDONLY)) == -1) {
                g_warning("Can't open mixer-device %s\n", setupdata.mix_device);
                return -1;
        }
       if (ioctl (mix, AUDIO_CONTROL, & acontrol) == -1) {
               g_warning("Error reading mixer\n");
               return -1;
       }
       close (mix);
#endif
#ifdef hpux
# ifndef hpux_alib
        dodebug(10,"setting mixer %s to %d\n", setupdata.mix_device, val);
	if ((mix = open(setupdata.mix_device, O_RDWR)) == -1) {
                g_warning("Can't open mixer-device %s\n", setupdata.mix_device);
                return -1;
        }
	/* Query the receive/transmit/monitor gains */
	again.channel_mask = AUDIO_CHANNEL_0 | AUDIO_CHANNEL_1;
	ioctl(mix,AUDIO_GET_GAINS,&again);

	/* Calculate the new transmit gain */
	again.cgain[0].transmit_gain = AUDIO_OFF_GAIN 
	        + (gfloat) ((AUDIO_MAX_GAIN - AUDIO_OFF_GAIN) * val) * 0.01;
	again.cgain[1].transmit_gain = again.cgain[0].transmit_gain;
	again.channel_mask = AUDIO_CHANNEL_0 | AUDIO_CHANNEL_1;

	/* Set the new gains (only transmit gain actually changed) */
	if (ioctl(mix, AUDIO_SET_GAINS, &again) == -1) {
	        g_warning("Error setting audio gain\n");
                return -1;
	}
	close(mix);
# else
	/*
	 * The audio transmission gains are set by some
	 * environment variables or a fancy program
	 * like the AudioCP
	 */
	return 0;
# endif
#endif

	return 0;	
}


/*
 * send SIGINT to a cdda2wav/readcd/cdrecord process (resp. to its child) 
 * and hope that it terminates at it own
 */
void kill_readcdda() {

	/* stop any timers watching for cdrecord output */
	if (cdrtimer) {
		g_source_remove(cdrtimer);
		cdrtimer = 0;
	}

	if (readcdda_pid != -1) {

		dodebug(2,"sending SIGINT to %d\n", (gint) readcdda_pid);
		kill(readcdda_pid, SIGINT);
	}
	if (readcdda_pid2 != -1) {

		dodebug(2,"sending SIGINT to %d\n", (gint) readcdda_pid2);
		kill(readcdda_pid2, SIGINT);
	}
}


/*
 * in bulk mode update the info-label of read tracks
 */
static void update_bulk_readlabel() {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gint frms;

	frms = writeparams.frames[read_tracknr];
	convert_frames2mbminstring(frms, tmp);
	g_snprintf(tmp2,MAXLINE,_("Reading audio track %d/%d [%s]"), read_tracknr,
			writeparams.nrtracks, tmp);
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), tmp2);
}


/*
 * parse output of cdda2wav and update sliders
 */
static gboolean read_cdda2wav_out_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar convtmp[MAXLINE];
gchar *p;
gint val, tnr, bulk;
gfloat pval, pval2;

	/* called for bulk or not? */
	bulk = GPOINTER_TO_INT(data);

	/* read output of cdda */
	read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

	strip_string(line);
	dodebug(10,"cdda2wav: %s\n", line);

	/* scanning for indexes? */
	if (strncmp(line,"index scan:", 11) == 0) {
		strcpy(tmp,line+11);
		p = strtok(tmp,".");
		if (p != NULL) {
			tnr = atoi(p);
		} else {
			tnr = 0;
		}
		g_snprintf(tmp,MAXLINE,_("Scanning track %d for indices..."),tnr);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);	
	}

	/* found indices? */
	if (strncmp(line,"track", 5) == 0) {
		strcpy(tmp,line);
		/* just output until first comma - looks nicer */
		p = strtok(tmp,",");
		if (p != NULL) {
			strcpy(tmp2,p);
		} else {
			strcpy(tmp2,tmp);
		}	
		strcat(tmp2,"\n");
		append_to_text_view(readtrack_textview, tmp2);
	}

	/* scanning for MCN? */
	if (strncmp(line,"scanning for MCN", 16) == 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),_("Scanning for MCN (Media Catalog Number)..."));	
	}	

	/* scanning for ISRC? */
	if (strncmp(line,"scanning for ISRCs:", 19) == 0) {
		strcpy(tmp,line+20);
		p = strtok(tmp,".");
		if (p != NULL) {
			tnr = atoi(p);
		} else {
			tnr = 0;
		}
		g_snprintf(tmp,MAXLINE,_("Scanning track %d for ISRC..."),tnr);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);	
	}
	
	/* done with scanning? */
	if (strncmp(line,"recording", 9) == 0) {
		if (bulk == 0) {
			/* restore original info text */
			gtk_label_set_text(GTK_LABEL(readtrack_info_label),
				readtrack_info_string);	
		} else {
			update_bulk_readlabel();
		}
	}

	/* look for a percent value */
	if (line[strlen(line)-1] == '%') {
		/* skip leading linefeed if any */
		if (line[0] == '\r') 
			line[0] = ' ';
	
		/* look for last space */
		p = rindex(line,' ');
		if (p != NULL) 
			strcpy(tmp,p);
		else 
			strcpy(tmp,line);

		tmp[strlen(tmp)-1] = '\0';	
		strip_string(tmp);
		val = atoi(tmp);
		pval = (gfloat)val/100;
		if (pval > 1.0) pval = 1.0;
		gtk_progress_set_percentage(GTK_PROGRESS(readtrack_pbar1),
			pval);

		/* now calculate how much percent he have at all */
		if (bulk == 0) {
			pval2 = (pct_so_far + (pval * pct_this_track)) /100;
			if (pval2 > 1.0) pval2 = 1.0;
		} else {
			pval2 = (writeparams.pct_so_far_arr[read_tracknr] +
				(pval * writeparams.pct_this_track_arr[read_tracknr]))/100;
		}
	
		gtk_progress_set_percentage(GTK_PROGRESS(readtrack_pbar2),
			pval2);
		set_xcdr_title(toplevel, viewmode_dialog, (gint)(pval2*100+0.5));

		/* now update info for small view */
		g_snprintf(tmp,MAXLINE,"%d%% / %d%%",(gint)(pval*100+0.5),
			   (gint)(pval2*100+0.5));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info2),tmp);	
		
		return (TRUE);
	}

	/* track recorded successfully or maybe with problems? */
	if (strstr(line,"recorded ")) {
		p = strstr(line,"100%  track ");
		if (p != NULL) {
			gtk_progress_set_percentage(GTK_PROGRESS(readtrack_pbar1), 1.0);
			strcpy(tmp,p+6);	/* +6 = offset for '100%  ' */
			strcpy(line,tmp);	

			if (bulk == 1) {
				/* in bulkmode this means we have to switch to next track */
				read_tracknr++;
				update_bulk_readlabel();
			}	
		}
	}
		
	/* get subprocess pid */
	if (strncmp(line,"child pid", 9) == 0) {
		strcpy(tmp, line+12);
		readcdda_pid= (pid_t) atoi(tmp);
		return (TRUE);
	}	
	
	/* error-text? do display these in the next block */
	if (strncmp(line,"Error",5) == 0 || strstr(line,"open audio sample file") != NULL) {
		read_output_ctrl = 1;
	}	

	/* forward most other output to textview-window */
	if (strncmp(line,"record",6) == 0 || read_output_ctrl == 1) {
		read_output_ctrl = 1;
		/* skip lines that do not interest us */
		if (strncmp(line,"percent_done",12) == 0) {
			return (TRUE);
		}
		if (strncmp(line,"overlap:min",11) == 0) {
			return (TRUE);
		}
		p = strstr(line,"100%  ");
		if (p != NULL) {
			strcpy(tmp, p+6);	/* +6 = offset for '100%  ' */
			strcpy(line, tmp);	
		}
		strcat(line,"\n");

		if (charset_convert_to_utf8(line, convtmp))
			strncpy(line, convtmp, MAXLINE);

		append_to_text_view(readtrack_textview, line);
	}	

	return (TRUE);
}

static gboolean read_cdda2wav_out(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	if (cond & G_IO_IN) {
            
            read_cdda2wav_out_io_in(source, cond, data);
    	}

	if ((cond & G_IO_IN) && (cond & G_IO_HUP)) {
                read_io_channel_all(source, line, MAXLINE);
		if (strcmp(line, "") == 0) {
			cond = G_IO_HUP;
		} else {
			append_multiline_to_textview(line, "100%  ", readtrack_textview, "cdda2wav-hup");
		}
    	}

	/* cdda-finished? */
	if (cond == G_IO_HUP) {

		/* tell our caller that we are done here */
		read_done = get_wait_ret(-1);
		return (FALSE);
	}
	return (TRUE);
}

/*
 * parse dummy output of cdda2wav
 * output stdout of cdda2wav where never should any data be delivered
 */
static gboolean read_cdda2wav_dummyout(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	/* read output of cdda */
	read_io_channel_all(source, line, MAXLINE);

	strcat(line,"\n");
	append_to_text_view(readtrack_textview, line);

	return (TRUE);
}


/*
 * call cdda2wav to read an audio-track 
 * return 0 on success, 1 on error
 */
gint read_audio_track(gint devnr, gint starttrack, gint endtrack, gint kbyte, 
		      gchar *fname, gint startoffset, gint endoffset,
		      gint nrtracks, gfloat percent, gfloat percent_done,
		      gint viewtrack) {
gchar cmd[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gchar tmp4[MAXLINE];
gchar tmp5[MAXLINE];
gchar tmp6[MAXLINE];
gchar tmp8[MAXLINE];
gchar tmp9[MAXLINE];
gchar tmpfname[MAXLINE];
gint read_in, read_out, read_dummy;
GIOChannel *gio, *gio2;

	/* if another read running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* no filename given? */
	if (fname == NULL) {
		return 1;
	}
	strncpy(tmpfname, fname, MAXLINE);

	/* mark our read-process as running */
	read_done = 999;
	read_output_ctrl = 0;
	readcdda_pid = -1;
	readcdda_pid2 = -1;
	read_abort_mark = 0;

	/* set info-label */
	convert_kbytes2mbminstring(kbyte, tmp);
	g_snprintf(tmp2,MAXLINE,_("Reading audio track %d/%d [%s]"), viewtrack, nrtracks, tmp);
	gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp2);	
	strcpy(readtrack_info_string, tmp2);

	g_snprintf(tmp2,MAXLINE,_("Reading %d/%d:"), viewtrack, nrtracks);
	gtk_label_set_text(GTK_LABEL(readtrack_small_info),tmp2);	
	
	/* get bus,id,lun string */
	if (convert_devnr2busid(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* some stuff to have our slider moving smoothly no matter how
	   big the tracks are */
	pct_so_far = percent_done;
	pct_this_track = percent;

	/* build command line */
	if (endtrack != 0) {
		g_snprintf(tmp2,MAXLINE,"%d+%d",starttrack,endtrack);
	} else {
		g_snprintf(tmp2,MAXLINE,"%d",starttrack);
	}

	/* set speed only when not zero */
	if (get_cur_audioread_speed() > 0) {
		g_snprintf(tmp3,MAXLINE,"-S %d", get_cur_audioread_speed()); 
	} else {
		strcpy(tmp3,"");
	}

	/* have to scan for indexes? */
	if (curset.indexscan) {
		strcpy(tmp4,"-i 1 -v all");
	} else {
		strcpy(tmp4,"-v toc,summary,sectors,titles");
	}

	/* have to deemphasize? */
	if (curset.deemphasize) {
		strcpy(tmp9,"-deemphasize");
	} else {
		strcpy(tmp9,"");
	}

	/* set paranoia options */
	if (get_cur_audioread_useparanoia() && (get_cur_audioread_paranoiaminoverlap() < 1)) {
		g_snprintf(tmp6, MAXLINE, "paraopts=retries=%d,readahead=%d",
			   get_cur_audioread_paranoiaretries(),get_cur_audioread_paranoiareadahead());
	} else if (get_cur_audioread_useparanoia()) {
		g_snprintf(tmp6, MAXLINE, "paraopts=retries=%d,readahead=%d,minoverlap=%d",
			   get_cur_audioread_paranoiaretries(),get_cur_audioread_paranoiareadahead(),get_cur_audioread_paranoiaminoverlap());
	} else {
		g_snprintf(tmp6, MAXLINE, "-P %d", get_cur_audioread_overlap());
	}

	/* have to enable -no-hidden-track? */
	if (get_cur_audioread_showhiddentrack()) {
		strcpy(tmp8,"");
	} else {
		strcpy(tmp8,"-no-hidden-track");
	}

	get_wrap_path("CDDA2WAV", tmp5);
	g_snprintf(cmd,MAXLINE,
		   "%s -D \"%s\" -g -no-textfile -O wav -t %s %s %s %s %s %s \"%s\"",
		   tmp5,tmp,tmp2,tmp3,tmp4,tmp6,tmp8,tmp9,
		   convert_escape(tmpfname));

	dodebug(1, "spawning: %s\n",cmd);
	dolog(2,"Read audio track %s\n", fname);
	dolog(3,"Executing: %s\n",cmd);

	/* start child and get new fds */
	readcdda_pid = full_dpl_pipe3(&read_dummy,&read_in,&read_out,cmd,0);
	readcdda_pid2 = -1;

	/* catch output of child */
	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_cdda2wav_out, GINT_TO_POINTER(0));

	gio2 = g_io_channel_unix_new(read_dummy);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback2 = g_io_add_watch(gio2, G_IO_IN,
                read_cdda2wav_dummyout, NULL);

	/* now wait until track is read */
	while (read_done == 999) {
		wait_and_process_events();
	}

	g_source_remove(readcdda_callback2);
	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);

	close(read_out);
	close(read_in);
	close(read_dummy);

	append_to_text_view(readtrack_textview, "\n");

	/* error while reading? */
	if (read_done != 0 && read_abort_mark == 0) {
		g_snprintf(tmp,MAXLINE,_("Error reading audio track %d/%d"), starttrack, nrtracks);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);	
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Read-Error:"));	
		return 1;
	}
	/* aborted? */
	if (read_abort_mark == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),_("Read aborted..."));	
		return 1;
	}

	return 0;
}


/*
 * call cdda2wav to read all audio tracks from a cd
 */
gint start_bulk_read_action(gint devnr, gfloat percent_done, gint startnr) {
GList *loop;
track_read_param_t *trackparam;
char tmp[MAXLINE];
char tmptmp[MAXLINE];
char tmp3[MAXLINE];
char tmp4[MAXLINE];
char tmp5[MAXLINE];
char tmp6[MAXLINE];
char tmp8[MAXLINE];
char tmp9[MAXLINE];
char cmd[MAXLINE*10];  /* extra big buffer for very long cdrecord options */
gint read_in, read_out, read_dummy;
gint tracknr;
GIOChannel *gio, *gio2;

        /* if another read running, ignore */
        if (read_done == 999) {
                return -1;
        }

        /* mark our read-process as running */
        read_done = 999;
        read_output_ctrl = 0;
	pct_so_far = percent_done;
	read_tracknr = startnr;
        readcdda_pid = -1;
	readcdda_pid2 = -1;
        read_abort_mark = 0;

	/* init track-label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Initializing Disc Reader..."));
	strcpy(readtrack_info_string, "");

        /* reset writeparams-arrays */
	/* let's reuse this structure - even when we just read now */
        g_free(writeparams.tracktype);
        g_free(writeparams.frames);
        g_free(writeparams.pct_so_far_arr);
        g_free(writeparams.pct_this_track_arr);
        writeparams.tracktype = g_new0(gint,MAXTRACKS);
        writeparams.frames = g_new0(gint,MAXTRACKS);
        writeparams.pct_so_far_arr = g_new0(gfloat,MAXTRACKS);
        writeparams.pct_this_track_arr = g_new0(gfloat,MAXTRACKS);
        writeparams.nrtracks = 0;
        writeparams.simulation = curset.writesimul;

        /* get bus,id,lun string */
        if (convert_devnr2busid(devnr,tmp) != 0) {
                g_error("non existing cdrom?");
        }

        /* set speed only when not zero */
        if (get_cur_audioread_speed() > 0) {
                g_snprintf(tmp3,MAXLINE,"-S %d", get_cur_audioread_speed()); 
        } else {
                strcpy(tmp3,"");
        }

        /* have to scan for indexes? */
        if (curset.indexscan) {
                strcpy(tmp4,"-v all");
        } else {
                strcpy(tmp4,"-v toc,summary,sectors,titles");
        }

        /* have to deemphasize? */
        if (curset.deemphasize) {
                strcpy(tmp9,"-deemphasize");
        } else {
                strcpy(tmp9,"");
        }

	/* set paranoia options */
	if (get_cur_audioread_useparanoia() && (get_cur_audioread_paranoiaminoverlap() < 1)) {
		g_snprintf(tmp6, MAXLINE, "paraopts=retries=%d,readahead=%d",
			   get_cur_audioread_paranoiaretries(),get_cur_audioread_paranoiareadahead());
	} else if (get_cur_audioread_useparanoia()) {
		g_snprintf(tmp6, MAXLINE, "paraopts=retries=%d,readahead=%d,minoverlap=%d",
			   get_cur_audioread_paranoiaretries(),get_cur_audioread_paranoiareadahead(),get_cur_audioread_paranoiaminoverlap());
	} else {
		g_snprintf(tmp6, MAXLINE, "-P %d", get_cur_audioread_overlap());
	}

	/* have to enable -no-hidden-track? */
	if (get_cur_audioread_showhiddentrack()) {
		strcpy(tmp8,"");
	} else {
		strcpy(tmp8,"-no-hidden-track");
	}

	get_wrap_path("CDDA2WAV", tmp5);
	g_snprintf(cmd,MAXLINE,
		   "%s -D \"%s\" -g -no-textfile -O wav %s %s %s %s %s -B",
		   tmp5,tmp,tmp3,tmp4,tmp6,tmp8,tmp9);

	tracknr = startnr;
	/* now add all track-filenames (only audio) */
        loop = g_list_first(trackreadset.trackparams);
        while(loop) {
                trackparam = loop->data;
                if (trackparam->trackfile != NULL && 
	            trackparam->tracktype == 1) {
			strcpy(tmptmp, trackparam->trackfile);
                        g_snprintf(tmp, MAXLINE, " \"%s\"",
                                convert_escape(tmptmp));
                        strcat(cmd, tmp);

			/* fill up percent values for percentbar */
			writeparams.tracktype[tracknr] = trackparam->tracktype;
			writeparams.frames[tracknr] = trackparam->frames;
			writeparams.pct_this_track_arr[tracknr] = trackparam->percent;
			writeparams.pct_so_far_arr[tracknr] = pct_so_far;
			pct_so_far += trackparam->percent;
			writeparams.nrtracks++;
			tracknr++;

                }
                loop = loop->next;
        }
	/* correct full nr of tracks (so we have the full count of tracks) */
	writeparams.nrtracks+=startnr-1;

        dodebug(1, "spawning: %s\n",cmd);
        dolog(2,"Read all audio tracks\n");
        dolog(3,"Executing: %s\n",cmd);

        /* start child and get new fds */
        readcdda_pid = full_dpl_pipe3(&read_dummy,&read_in,&read_out,cmd,0);
	readcdda_pid2 = -1;

        /* catch output of child */
	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_encoding(gio, NULL, NULL); 
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_cdda2wav_out, GINT_TO_POINTER(1));

	gio2 = g_io_channel_unix_new(read_dummy);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback2 = g_io_add_watch(gio2, G_IO_IN,
                read_cdda2wav_dummyout, NULL);

        /* now wait until track is read */
        while (read_done == 999) {
                wait_and_process_events();
        }

	g_source_remove(readcdda_callback2);
	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);

        close(read_out);
        close(read_in);
	close(read_dummy);

	append_to_text_view(readtrack_textview, "\n");

        /* error while reading? */
        if (read_done != 0 && read_abort_mark == 0) {
                g_snprintf(tmp,MAXLINE,_("Error reading audio track %d/%d"), read_tracknr, 
			writeparams.nrtracks);
                gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);        
                gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Read-Error:"));  
                return 1;
        }
        /* aborted? */
        if (read_abort_mark == 1) {
                gtk_label_set_text(GTK_LABEL(readtrack_info_label),_("Read aborted..."));  
                return 1;
        }

	return 0;
}


/*
 * parse output of readcd and update sliders
 */
static gboolean read_readcd_out_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gint val;
gfloat pval, pval2;

	/* read output of readcd */
	read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

	dodebug(10,"readcd: %s\n", line);

	/* look for end value */
	if (strncmp(line,"end:",4) == 0) {
		strcpy(tmp,line+4);
		strip_string(tmp);
		readcd_endsector = atoi(tmp);
		return (TRUE);
	}

	/* look for a percent value */
	if (strncmp(line,"addr:",5) == 0) {
		/* skip leading linefeed if any */
		if (line[0] == '\r') 
			strcpy(tmp,line+6);
		else 
			strcpy(tmp,line+5);

		strip_string(tmp);
		strcpy(tmp2,strtok(tmp," "));
		val = atoi(tmp2) - readcd_startsector;

		/* if not set yet, do nothing */
		if (readcd_endsector == 0) 
			return (TRUE);

		pval = (gfloat)val/(gfloat)(readcd_endsector - readcd_startsector);
		if (pval > 1.0) pval = 1.0;

		gtk_progress_set_percentage(GTK_PROGRESS(readtrack_pbar1),
			pval);

		/* now calculate how much percent he has at all */
		pval2 = (pct_so_far + (pval * pct_this_track)) /100;
		if (pval2 > 1.0) pval2 = 1.0;
		gtk_progress_set_percentage(GTK_PROGRESS(readtrack_pbar2),
			pval2);
		set_xcdr_title(toplevel,viewmode_dialog,(gint)(pval2*100+0.5));

		/* now update info for small view */
		g_snprintf(tmp,MAXLINE,"%d%% / %d%%",(gint)(pval*100+0.5),
			   (gint)(pval2*100+0.5));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info2),tmp);	
		
		return (TRUE);
	}

	/* look if last expected line from readcd came */
	if (strncmp(line,"Read",4) == 0) {
		/* ok...now we can expect that we were not aborted */
		if (read_output_ctrl != 2) {
			read_output_ctrl = 1;
		}
	}

	/* look if we got an read error */
	if (line[0] == '.') {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),_("Failed to read sector - retrying"));	
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Retrying..."));	
	}

	/* now look if the got an error which could not corrected */
	if (strstr(line,"not corrected")) {
		read_output_ctrl = 2;
	}

	/* write error on hard disk? */
	if (strstr(line,"annot write")) {
		read_output_ctrl = 2;
	}

        /* some blocked output of tracks came through */
        if (strstr(line, " cnt: ")) {
                /* kick it */
                return (TRUE);
        }

	/* forward most other output to textview window */
	strcat(line,"\n");
	append_to_text_view(readtrack_textview, line);

	return (TRUE);
}

static gboolean read_readcd_out(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];
gint ret;

    	if (cond & G_IO_IN) {
        
        	read_readcd_out_io_in(source, cond, data);
    	}

	if ((cond & G_IO_IN) && (cond & G_IO_HUP)) {
                read_io_channel_all(source, line, MAXLINE);
		if (strcmp(line, "") == 0) {
			cond = G_IO_HUP;
		} else {
			append_multiline_to_textview(line, "", readtrack_textview, "readcd-hup");
		}
    	}

	/* readcd-finished? */
	if (cond == G_IO_HUP) {

		/* tell our caller that we are done here */
		ret = get_wait_ret(-1);
		if (ret == 0 && read_output_ctrl == 0) {
			/* readcd does return status 0 when killed - override */
			read_done = 1;
		} else {
			read_done = ret;
		}
		return (FALSE);
	}

    return(TRUE);
}

/*
 * call readcd to read a data-track 
 * return 0 on success, 1 on error
 */
gint read_data_track(gint devnr, gint starttrack, gint kbyte, 
		      gchar *fname, gint startoffset, gint endoffset,
		      gint nrtracks, gfloat percent, gfloat percent_done,
		      gint viewtrack) {
gchar cmd[MAXLINE];
gchar tmp[MAXLINE];
gchar tmptmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gint read_in, read_out, sectsize;
GIOChannel *gio;

	/* if another read running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* no filename given? */
	if (fname == NULL) {
		return 1;
	}

	/* mark our read-process as running */
	read_done = 999;
	readcdda_pid = -1;
	readcdda_pid2 = -1;
	read_output_ctrl = 0;
	readcd_startsector = startoffset;
	readcd_endsector = 0;
	read_abort_mark = 0;

	/* set info-label */
	convert_kbytes2mbminstring(kbyte, tmp);
	g_snprintf(tmp2,MAXLINE,_("Reading data track %d/%d [%s]"), viewtrack, nrtracks, tmp);
	gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp2);	

	g_snprintf(tmp2,MAXLINE,_("Reading %d/%d:"), viewtrack, nrtracks);
	gtk_label_set_text(GTK_LABEL(readtrack_small_info),tmp2);	
	
	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* some stuff to have our slider moving smoothly no matter how
	   big the tracks are */
	pct_so_far = percent_done;
	pct_this_track = percent;

        /* now check which sectorsize we got */
        sectsize = get_sectorsize(devnr);
	if (sectsize == 2352) {
		/* workaround for buggy drives */
        	endoffset = ((endoffset * DATASECTORSIZE) / sectsize) + 1;
	}

	get_wrap_path("READCD", tmp3);
	strcpy(tmptmp,fname);
	g_snprintf(cmd,MAXLINE,
		   "%s %s sectors=%d-%d -s retries=32 f= \"%s\"",
		   tmp3, tmp, startoffset, endoffset, 
		   convert_escape(tmptmp));
	dodebug(1, "spawning: %s\n",cmd);
	dolog(2,"Read data track %s\n", fname);
	dolog(3,"Executing: %s\n",cmd);

	/* start child and get new fds */
	readcdda_pid = full_dpl_pipe3(NULL,&read_in,&read_out,cmd,0); 
	readcdda_pid2 = -1;

	/* catch output of child */
	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_encoding(gio, NULL, NULL); 
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
		read_readcd_out, NULL);

	/* now wait until track is read */
	while (read_done == 999) {
		wait_and_process_events();
	}

	g_io_channel_unref(gio);
	close(read_out);
	close(read_in);

	append_to_text_view(readtrack_textview, "\n");

	/* error while reading? */
	if ((read_done != 0 && read_abort_mark == 0) || read_output_ctrl == 2) {
		g_snprintf(tmp,MAXLINE,_("Error reading data track %d/%d"), starttrack, nrtracks);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);	
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Read-Error:"));	
		return 1;
	}
	/* aborted? */
	if (read_abort_mark == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),_("Read aborted..."));	
		return 1;
	}

	return 0;
}


static gboolean checkmedium_out_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gint devnr, sectsize;
gchar tmp[MAXLINE];
gchar line[MAXLINE];
gchar *p;

        /* read output of readcd */
	read_io_channel(source, line, MAXLINE);

	dodebug(10,"check_medium_loaded: %s\n", line);
	/*
	 * got the message that no medium is present?
	 * in this case, ignore return code
	 */
	if (strstr(line,"medium not present")) {
		checkmedium_found = 1;
	}
	if (strstr(line,"Device not ready")) {
                checkmedium_found = 1;
        }

        /* get a line about sectorsize? */
        if (strstr(line,"Sectorsize:")) {
                /* extract the bytes given there */
                strcpy(tmp,line+12);
                p = strtok(tmp," ");
                if (p) {
                        sectsize = atoi(p);
                        dodebug(10,"-> Detected sectorsize of %d bytes\n", sectsize);
			devnr = GPOINTER_TO_INT(data);
                        set_sectorsize(devnr,sectsize);
                }
        }
	return (TRUE);
}

static gboolean checkmedium_out(GIOChannel *source, GIOCondition cond, gpointer data) {

	if (cond & G_IO_IN) {

        	checkmedium_out_io_in(source, cond, data);
    	}

    	/* readcd finished */
	if (cond == G_IO_HUP) {

                /* tell our caller that we are done here */
                read_done = get_wait_ret(-1);
                return (FALSE);
        }
    return (TRUE);
}

/*
 * checks if a disc is loaded in drive 
 * return 0 if not, 1 if loaded and ready
 */
static gint check_medium_loaded(gint devnr) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar line[MAXLINE];
gint read_out;
GIOChannel *gio;

	checkmedium_found = 0;
	read_done = 999;

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* build command line */
	get_wrap_path("READCD", tmp2);
	g_snprintf(line,MAXLINE,"%s %s f=- sectors=0-0",
		tmp2,tmp);

	dodebug(1, "spawning: %s\n", line);

        readcdda_pid = full_dpl_pipe3(NULL,NULL,&read_out,line,0);
        readcdda_pid2 = -1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_encoding(gio, NULL, NULL); 
	readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
		checkmedium_out, GINT_TO_POINTER(devnr));

        /* now wait until toc is read */
        while (read_done == 999) {
                wait_and_process_events();
        }

	g_io_channel_unref(gio);
        close(read_out);

	dodebug(10, "medium loaded return code: %d\n", read_done);

	if ((read_done == 0 || read_done == 255) && checkmedium_found == 0) 
		return 1;
	else
		return 0;
}


/*
 * save the contents of a text-widget to a file 
 * return 0 on success, 1 on error
 */
gint save_text2file(char *fname, GtkWidget *txt) {
gchar *buf;
GtkTextBuffer *buffer;
GtkTextIter start, end;
gint fd;

	/* get text from textwidget */
	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
	gtk_text_buffer_get_bounds(buffer, &start, &end);
	buf = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);

	dodebug(2, "saving extended output to %s\n", fname);
	dolog(3, "Saving extended output to %s\n", fname);

	/* write to file */
	fd = open(fname, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
	if (fd < 0) {
		g_warning("Can't open file %s for writing\n",fname);
		return 1;
	}

	if (write(fd,buf,strlen(buf)) != strlen(buf)) {
		g_warning("Can't write file %s\n",fname);
		return 1;
	}

	g_free(buf);
	close(fd);
	chmod(fname,0644);

	return 0;
}


/*
 * write the toc-file when copying full discs
 */
gint write_copy_cd_toc_file(gchar *tocfile) {
GList *loop;
track_read_param_t *trackparam;
FILE *fd;
time_t acttime;
gchar timestr[MAXLINE];
gint tracknr;
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];

	if (tocfile == NULL) return 1;

	dodebug(2, "writing Disc toc file %s\n", tocfile);

        /* is a link? */
        if (check_islink(tocfile, NULL)) {
        	g_warning("Warning, won't overwrite a link at %s\n", tocfile);
		return 1;
        }

	/* write to file */
	fd = fopen(tocfile,"w"); 

	if (fd == NULL) {
		g_warning("Can't open file %s for writing\n",tocfile);
		return 1;
	}

	/* get current time */
	acttime = time((time_t *) 0);
	strcpy(timestr,ctime(&acttime));
	timestr[strlen(timestr)-1] = '\0';

	/* write header */
	fputs("#\n",fd);
	g_snprintf(tmp,MAXLINE,"# X-CD-Roast %s - TOC-File\n",XCDROAST_VERSION);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"# created: %s\n", timestr);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"# by: %s@%s\n", username, hostname);
	fputs(tmp,fd);
	fputs("#\n",fd);

	/* write disc info */
	convert_frames2minstring(cdinfo.total_size, tmp2);
	g_snprintf(tmp,MAXLINE,"# Tracks: %d [%s]\n", 
		cdinfo.nr_tracks, tmp2);
	fputs(tmp,fd);
	if (cdinfo.title && cdinfo.artist) {
		g_snprintf(tmp,MAXLINE,"# title/artist: %s / %s\n",
			cdinfo.title, cdinfo.artist);
		fputs(tmp,fd);
	}
	if (cdinfo.cddb_dtitle != NULL) {
		g_snprintf(tmp,MAXLINE,"# cddb: %s\n", cdinfo.cddb_dtitle);
		fputs(tmp,fd);
	}
	fputs("#\n",fd);

	/* generate disc title */
	if (cdinfo.cddb_dtitle != NULL) {
		strcpy(tmp2,cdinfo.cddb_dtitle);
		g_snprintf(tmp,MAXLINE,"cdtitle = \"%s\"\n", convert_escape(tmp2));
	} else 
	if (cdinfo.title && cdinfo.artist && cdinfo.title[0] != '\0') {
		g_snprintf(tmp2,MAXLINE,"%s / %s", 
			cdinfo.title, cdinfo.artist);
		g_snprintf(tmp,MAXLINE,"cdtitle = \"%s\"\n", convert_escape(tmp2));
	} else {
		/*
		 * no title available?
		 * if data-track get track-label (better than nothing)
		 * get first track
		 */
		loop = g_list_first(trackreadset.trackparams);
		if (loop) {
			trackparam = loop->data;
			tracknr = trackparam->trackinfo_index;
			if (trackinfo[tracknr]->title != NULL) {
				strcpy(tmp2,trackinfo[tracknr]->title);
			} else {
				strcpy(tmp2,"");
			}
			g_snprintf(tmp,MAXLINE,"cdtitle = \"%s\"\n", 
				convert_escape(tmp2));
		} else {	
			g_snprintf(tmp,MAXLINE,"cdtitle = \"\"\n");
		}
	}	
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"cdsize = %d\n",cdinfo.total_size);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"discid = \"%s\"\n",cdinfo.cddb_discid);
	fputs(tmp,fd);


	/* write a section for each track */
	loop = g_list_first(trackreadset.trackparams);
	while (loop) {
		trackparam = loop->data;

		tracknr = trackparam->trackinfo_index;

		/* make sure to get some NULLs wasted */
		if (trackinfo[tracknr]->title == NULL) {
			trackinfo[tracknr]->title = g_strdup("");
		}
		if (trackinfo[tracknr]->artist == NULL) {
			trackinfo[tracknr]->artist = g_strdup("");
		}

		fputs("\n",fd);
		if (trackinfo[tracknr]->type == 0) {
			convert_frames2mbstring(trackinfo[tracknr]->size, tmp2);
			g_snprintf(tmp,MAXLINE,"# data: %s [%"LL_FORMAT" bytes / %s]\n",
				trackinfo[tracknr]->title,
				(gint64) trackinfo[tracknr]->size * DATASECTORSIZE,
				tmp2);
			fputs(tmp,fd);
		} else {
			convert_frames2minstring(trackinfo[tracknr]->size, tmp2);
			g_snprintf(tmp,MAXLINE,"# audio: %s / %s [%"LL_FORMAT" bytes / %s]\n",
				trackinfo[tracknr]->title,
				trackinfo[tracknr]->artist,
				(gint64) trackinfo[tracknr]->size * CDDAFRAME, tmp2);
			fputs(tmp,fd);
			if (trackinfo[tracknr]->cddb_ttitle != NULL) {
				g_snprintf(tmp,MAXLINE,"# cddb: %s\n",
					trackinfo[tracknr]->cddb_ttitle);
				fputs(tmp,fd);
			}
		}

		g_snprintf(tmp,MAXLINE,"track = %02d\n", trackparam->starttrack);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"type = %d\n", trackinfo[tracknr]->type);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"size = %d\n", trackinfo[tracknr]->size);
		fputs(tmp,fd);
		g_snprintf(tmp,MAXLINE,"startsec = %d\n", trackinfo[tracknr]->start_sec);
		fputs(tmp,fd);

		if (trackparam->trackfile != NULL) {
			strcpy(tmp2, trackparam->trackfile);
		} else {
			strcpy(tmp2,"");
		}
		g_snprintf(tmp,MAXLINE,"file = \"%s\"\n", 
			convert_escape(tmp2));
		fputs(tmp,fd);

		loop = loop->next;
	}

	if (fclose(fd) != 0) {
		/* error closing file */
		return 1;
	}

	return 0;
}


/*
 * print trackparams-structure for debug reasons
 */
void print_trackreadset() {
GList *loop;
track_read_param_t *trackparam;

	dodebug(2,"------ trackreadset-structure -----\n");
	dodebug(2,"nrtracks: %d\n",trackreadset.nrtracks);
	dodebug(2,"cdsize: %d\n",trackreadset.cdsize);
	dodebug(2,"tocfile: %s\n",trackreadset.tocfile);
	dodebug(2,"cdtitle: %s\n",trackreadset.cdtitle);

	loop = g_list_first(trackreadset.trackparams);
	while(loop) {
		trackparam = loop->data;
		dodebug(2,"\ntrackinfo_index: %d\n", trackparam->trackinfo_index);
		dodebug(2,"starttrack: %d\n", trackparam->starttrack);
		dodebug(2,"endtrack: %d\n", trackparam->endtrack);
		dodebug(2,"tracktype: %d\n", trackparam->tracktype);
		dodebug(2,"start_sec: %d\n", trackparam->start_sec);
		dodebug(2,"startoffset: %d\n", trackparam->startoffset);
		dodebug(2,"endoffset: %d\n", trackparam->endoffset);
		dodebug(2,"percent: %f\n", trackparam->percent);
		dodebug(2,"kbyte: %d\n", trackparam->kbyte);
		dodebug(2,"frames: %d\n", trackparam->frames);
		if (trackparam->trackfile != NULL) {
			dodebug(2,"trackfile: %s\n", trackparam->trackfile);
		} else {
			dodebug(2,"trackfile: (NULL)\n");
		}	
		loop = loop->next;
	}
}


/*
 * read the toc-file when copying full discs
 */
gint read_copy_cd_toc_file(gchar *tocfile) {
FILE *fd;
GList *loop;
track_read_param_t *trackparam;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];
gint cdsize;
gfloat per;

	/* keep compiler shut about uninitialized warning */
	trackparam = NULL;

	/* prepare trackreadset for refilling */
	loop = g_list_first(trackreadset.trackparams);
	while (loop) {
		trackparam = loop->data;
		g_free(trackparam->trackfile);
		g_free(trackparam);
		loop = loop->next;
	}
	g_list_free(trackreadset.trackparams);
	trackreadset.trackparams = NULL;

	trackreadset.nrtracks = 0;
	trackreadset.cdsize = 0;
	cdsize = 0;
	g_free(trackreadset.tocfile);
	trackreadset.tocfile=g_strdup(tocfile);
	g_free(trackreadset.cdtitle);
	trackreadset.cdtitle = g_strdup("");

	dodebug(2, "reading Disc toc file %s\n", tocfile);
	/* open file */
	fd = fopen(tocfile,"r");

	if (fd == NULL) {
		g_warning("Can't open file %s for reading\n",tocfile);
		return 1;
	}

	for (;;) {
		if (fgets(line,MAXLINE,fd) == NULL)
			break;

		dodebug(10,"read tocfile: %s", line);
		/* skip empty or hashed lines */
		strip_string(line);
		if (*line == '#' || *line == '\0')
			continue;

                /* parse lines */
		if (parse_config_line(line,id,value)) {
			dodebug(10,"invalid line in tocfile");
			fclose(fd);
			return 1;
		}	

		if (strcmp("cdtitle",id) == 0) {
			g_free(trackreadset.cdtitle);
			trackreadset.cdtitle = g_strdup(value);
		}
		if (strcmp("discid",id) == 0) {
			g_free(trackreadset.cd_discid);
			trackreadset.cd_discid = g_strdup(value);
		}
		if (strcmp("cdsize",id) == 0) {
			trackreadset.cdsize = atoi(value);
		}
		if (strcmp("track",id) == 0) {
			/* allocate memory */
			trackparam = g_new0(track_read_param_t,1);
			trackparam->trackinfo_index = -1;
			trackparam->starttrack = atoi(value);
			trackparam->endtrack = 0;
			trackreadset.nrtracks++; 	
		}
		if (strcmp("type",id) == 0) {
			trackparam->tracktype = atoi(value);
		}
		if (strcmp("size",id) == 0) {
			trackparam->frames = atoi(value);
			cdsize += trackparam->frames;
		}
		if (strcmp("startsec",id) == 0) {
			trackparam->start_sec = atoi(value);
		}
		if (strcmp("file",id) == 0) {
			g_free(trackparam->trackfile);
			trackparam->trackfile = g_strdup(value); 
			
			trackreadset.trackparams = g_list_append(
				trackreadset.trackparams, trackparam);
		}

	}

	if (fclose(fd) != 0) {
		/* error closing file */
		return 1;
	}

	/* ok..all data read - now calculate the percentages */
	loop = g_list_first(trackreadset.trackparams);
	while(loop) {
		trackparam = loop->data;
		per = (gfloat)trackparam->frames / cdsize;
		trackparam->percent = per;

		loop = loop->next;
	}

	if (debug > 1) 
		print_trackreadset();

	return 0;
}

	
/*
 * write the info-file for each read track
 */
gint write_inf_file(track_read_param_t *trackparam) {
FILE *fd;
time_t acttime;
gchar timestr[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p;
gint tracknr;

	/* get current time */
	acttime = time((time_t *) 0);
	strcpy(timestr,ctime(&acttime));
	timestr[strlen(timestr)-1] = '\0';

	if (trackparam->trackfile == NULL) {
		return 1;
	}

	/*
	 * generate filename:
	 * remove extension and put .xinf there
	 */
	strncpy(tmp,trackparam->trackfile,MAXLINE - 4);
	p = rindex(tmp,'.');
	if (!p) return 1;
	*p = '\0';
	strcpy(tmp2,tmp);
	strcat(tmp2,XCDROAST_INFO_EXT);

	dodebug(1,"writing inffile %s\n", tmp2);

        /* is a link? */
        if (check_islink(tmp2, NULL)) {
        	g_warning("Warning, won't overwrite a link at %s\n", tmp2);
		return 1;
        }

	/* write to file */
	fd = fopen(tmp2,"w"); 

	if (fd == NULL) {
		g_warning("Can't open file %s for writing\n",tmp2);
		return 1;
	}

	/* write header */
	fputs("#\n",fd);
	g_snprintf(tmp,MAXLINE,"# X-CD-Roast %s - INF-File\n",XCDROAST_VERSION);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"# created: %s\n", timestr);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"# by: %s@%s\n", username, hostname);
	fputs(tmp,fd);
	fputs("#\n",fd);

	tracknr = trackparam->trackinfo_index;
	
	g_snprintf(tmp,MAXLINE,"file = \"%s\"\n",trackparam->trackfile);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"track = %d of %d\n",
			trackinfo[tracknr]->track_nr,
			cdinfo.nr_tracks);
	fputs(tmp,fd);
	if (trackinfo[tracknr]->title) {
		strcpy(tmp2,trackinfo[tracknr]->title);
	} else {
		strcpy(tmp2,"");
	}
	g_snprintf(tmp,MAXLINE,"title = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	if (trackinfo[tracknr]->artist) {
		strcpy(tmp2,trackinfo[tracknr]->artist);
	} else {
		strcpy(tmp2,"");
	}
	g_snprintf(tmp,MAXLINE,"artist = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	if (trackinfo[tracknr]->cddb_ttitle != NULL) {
		strcpy(tmp2,trackinfo[tracknr]->cddb_ttitle);
		g_snprintf(tmp,MAXLINE,"cddb_ttitle = \"%s\"\n",
			convert_escape(tmp2));
		fputs(tmp,fd);
	}
	g_snprintf(tmp,MAXLINE,"size = %d\n", trackinfo[tracknr]->size);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"type = %d\n", trackinfo[tracknr]->type);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"rec_type = %d\n", trackinfo[tracknr]->rec_type);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"preemp = %d\n", trackinfo[tracknr]->preemp);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"copyperm = %d\n", trackinfo[tracknr]->copyperm);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"stereo = %d\n", trackinfo[tracknr]->stereo);
	fputs(tmp,fd);
	fputs("# from CD:\n",fd);
	if (cdinfo.title) {
		strcpy(tmp2,cdinfo.title);
	} else {
		strcpy(tmp2,"");
	}
	g_snprintf(tmp,MAXLINE,"cd_title = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	if (cdinfo.artist) {
		strcpy(tmp2,cdinfo.artist);
	} else {
		strcpy(tmp2,"");
	}
	g_snprintf(tmp,MAXLINE,"cd_artist = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	if (cdinfo.cddb_dtitle != NULL) {
		strncpy(tmp2,cdinfo.cddb_dtitle, MAXLINE);
		g_snprintf(tmp,MAXLINE,"cd_cddb_dtitle = \"%s\"\n",
			convert_escape(tmp2));
		fputs(tmp,fd);
	}

	strncpy(tmp2,cdinfo.cddb_discid, MAXLINE);
	g_snprintf(tmp,MAXLINE,"cd_discid = \"%s\"\n",
		convert_escape(tmp2));
	fputs(tmp,fd);

	if (fclose(fd) != 0) {
		/* error closing file */
		return 1;
	}

	return 0;
}


/*
 * read the titles/artists from an inf-file
 */
gint get_inf_tracktitle(gchar *path, image_files_t *entry) {
FILE *fd;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p;
gint istoc;

	/*
	 * generate filename:
	 * remove extension and put .xinf there
	 */
	istoc = 0;
	strncpy(tmp,path,MAXLINE - 4);
	p = rindex(tmp,'.');
	if (!p) return 1;
	if (strcmp(p,".toc") == 0) {
		/* we are currently handling a toc file */
		istoc = 1;
	}
	*p = '\0';
	strcpy(tmp2,tmp);
	strcat(tmp2,XCDROAST_INFO_EXT);

	if (istoc) 
		strcpy(tmp2,path);

	dodebug(1, "reading titles from %s\n", tmp2);

	if ((fd = fopen(tmp2,"r")) == NULL) { 
		return 1;
	}

	for (;;) {
		if (fgets(line,MAXLINE,fd) == NULL)
			break;

		dodebug(10,"inffile: %s", line);

		/* skip empty or hashed lines */
		strip_string(line);
		if (*line == '#' || *line == '\0')
			continue;

                /* parse lines */
		if (parse_config_line(line,id,value)) {
			dodebug(10,"invalid line in infofile");
			fclose(fd);
			return 1;
		}	

		/* if we are handling a toc file only the title entry
		   is interesting */
		if (istoc) {
			if (strcmp("cdtitle",id) == 0) {
				g_free(entry->cddb_ttitle);
				entry->cddb_ttitle = g_strdup(value);
				break;
			}
		}

		if (strcmp("track",id) == 0) {
			/* extract track nr */
			p = strtok(value," ");
			if (p != NULL) {
				entry->from_track = atoi(p);
			}
		}

		if (strcmp("title",id) == 0) {
			g_free(entry->title);
			entry->title = g_strdup(value);
		}
		if (strcmp("artist",id) == 0) {
			g_free(entry->artist);
			entry->artist = g_strdup(value);
		}
		if (strcmp("cddb_ttitle",id) == 0) {
			g_free(entry->cddb_ttitle);
			entry->cddb_ttitle = g_strdup(value);
		}
		if (strcmp("cd_discid",id) == 0) {
			g_free(entry->cd_discid);
			entry->cd_discid = g_strdup(value);
		}
		if (strcmp("last_session_start",id) == 0) {
			entry->last_session_start = atoi(value);
		}
		if (strcmp("next_session_start",id) == 0) {
			entry->next_session_start = atoi(value);
		}
	}

	if (fclose(fd) != 0) {
		return 1;
	}

	return 0;
}


/*
 * call cdrecord -prcap
 */
void fill_device_info(gint devnr, GtkWidget *txt) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
FILE *fpin;
gchar *p;
gint errcount;

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* build command line */
	get_wrap_path("CDRECORD", tmp3);
	g_snprintf(line,MAXLINE,"%s %s -prcap 2>&1",
		tmp3,tmp);

	dodebug(1, "calling: %s\n", line);
	dolog(3, "Executing: %s\n", line);

        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	errcount = 0;
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		strcpy(tmp,line);

		dodebug(10, "prcap: %s", line);

		/* error condition? */
		p = strtok(tmp,":");
		if (p != NULL) {
			p = strtok(NULL,"");
			if (p != NULL) {
				strcpy(tmp2,p);
				strip_string(tmp2);
				if (strncmp(tmp2,"I/O error",9) == 0) {
					errcount++;
					/* more then once an i/o-error? */
					if (errcount > 1) 
						break; 
				}
			}
		}	
		append_to_text_view(txt, line);
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }
}


/*
 * eject disc in drive
 */
void eject_cd(gint devnr) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
FILE *fpin;
gchar *p;
gint errcount;

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* build command line */
	get_wrap_path("CDRECORD", tmp3);
	g_snprintf(line,MAXLINE,"%s %s -eject 2>&1",
		tmp3,tmp);

	dodebug(1, "calling: %s\n", line);

        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	/* do actually nothing here ... */
	errcount = 0;
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		strcpy(tmp,line);

		/* error condition? */
		p = strtok(tmp,":");
		if (p != NULL) {
			p = strtok(NULL,"");
			if (p != NULL) {
				strcpy(tmp2,p);
				strip_string(tmp2);
				if (strncmp(tmp2,"I/O error",9) == 0) {
					errcount++;
					/* more then once an i/o-error? */
					if (errcount > 1) 
						break; 
				}
			}
		}	
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }
}


/*
 * load disc in drive
 */
void load_cd(gint devnr) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
FILE *fpin;
gchar *p;
gint errcount;

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* build command line */
	get_wrap_path("CDRECORD", tmp3);
	g_snprintf(line,MAXLINE,"%s %s -load 2>&1",
		tmp3,tmp);

	dodebug(1, "calling: %s\n", line);

        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	/* do actually nothing here ... */
	errcount = 0;
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		strcpy(tmp,line);

		/* error condition? */
		p = strtok(tmp,":");
		if (p != NULL) {
			p = strtok(NULL,"");
			if (p != NULL) {
				strcpy(tmp2,p);
				strip_string(tmp2);
				if (strncmp(tmp2,"I/O error",9) == 0) {
					errcount++;
					/* more then once an i/o-error? */
					if (errcount > 1) 
						break; 
				}
			}
		}	
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }
}


/*
 * use cdrecord to get the leadout of a TOC
 * (used for DVDs where cddawav fails )
 */
static gint get_cdrecord_toc(gint devnr) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gchar *p;
FILE *fpin;
gint found;

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	found = 0;
	
	/* build command line */
	get_wrap_path("CDRECORD", tmp3);
	g_snprintf(line,MAXLINE,"%s %s -toc 2>&1",
		tmp3,tmp);

	dodebug(1, "calling: %s\n", line);

        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		strcpy(tmp,line);
		dodebug(10, "cdrecord -toc: %s", line);
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	/* tmp is now the last line of the output */
	if (strncmp(tmp,"track:lout", 10) == 0) {

		/* extract leadout sector */
		p = strtok(tmp,"(");
		if (p) {
			strncpy(tmp2,p,MAXLINE);
			p = rindex(tmp2,':');
			if (p) {
				strcpy(tmp,p+1);
				strip_string(tmp);
				found = atoi(tmp);
			}
		}		
	}

	return found;
}


/*
 * call cdrecord -atip
 */
void get_atip_info(gint devnr, GtkWidget *txt) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gchar *p;
FILE *fpin;
gint found_atip,errcount;

	found_atip = 0;

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

       	get_wrap_path("CDRECORD", tmp3);
	g_snprintf(line,MAXLINE,"%s %s -atip 2>&1",
		tmp3,tmp);

	dodebug(1,"calling: %s\n", line);
	dolog(3, "Executing: %s\n", line);

        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	/* syntax for dvd-atip is different */
	if (is_dvdwriter(curset.writer_devnr)) {
		found_atip = 1;
	}

	errcount = 0;
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		strcpy(tmp,line);

		dodebug(10,"atip: %s", line),

		/* error condition? */
		p = strtok(tmp,":");
		if (p != NULL) {
			p = strtok(NULL,"");
			if (p != NULL) {
				strcpy(tmp2,p);
				strip_string(tmp2);
				if (strncmp(tmp2,"I/O error",9) == 0) {
					errcount++;
					/* more then once an i/o-error? */
					if (errcount > 1) 
						break; 
				}
			}
		}	

		/* look for atip-signature */
		if (strncmp(line,"ATIP",4) == 0) {
			found_atip = 1;
		}
		if (found_atip) {
			append_to_text_view(txt, line);
		}
        }

	/* no atip-information available? */
	if (found_atip == 0) {
		/* generate error-message */
		strncpy(line,_("\nNo ATIP-Information available.\n\n(Possible reasons: No Disc loaded or your device\n does not support reading ATIP info)"),MAXLINE);

		append_to_text_view(txt, line);
	}

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }
}


/*
 * get output of cdrecord -msinfo
 */
static gboolean read_msinfo_out_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gint nr1, nr2;
gchar *p;

	/* read output of readcd */
	read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

        dodebug(10, "msinfo (stdout): %s\n", line);

         /* look for the reload media command */
         if (strcmp(line,"Re-load disk and hit <CR>") == 0) {
                cdrecord_reload = 1;
		return (TRUE);
	}

	strcpy(tmp, line);

	/* parse msinfo output */
	p = strtok(tmp,",");
	if (p) {
		nr1 = atoi(p);
		p = strtok(NULL,"");
		if (p) {
			nr2 = atoi(p);

			/* if parsing was sucessful use new values */
			msinfo_nr1 = nr1;
			msinfo_nr2 = nr2;
		}
	}
	return (TRUE);
}

static gboolean read_msinfo_out(GIOChannel *source, GIOCondition cond, gpointer data) {

	if (cond & G_IO_IN) {

        	read_msinfo_out_io_in(source, cond, data);
    	}

    	/* finished? */
	if (cond & G_IO_HUP) {
		if (read_done == 999) {
        		read_done = get_wait_ret(-1);
			dodebug(10,"Finished in read_msinfo_out with status: %d\n", 
				read_done);
		}
		return (FALSE);
	}
    	return (TRUE);
}

static gboolean read_msinfo_err_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	/* read output of readcd */
	read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

        dodebug(10, "msinfo (stderr): %s\n", line);

	return (TRUE);
}

/*
 * get output of cdrecord -msinfo (stderr)
 */
static gboolean read_msinfo_stderr(GIOChannel *source, GIOCondition cond, gpointer data) {

	if (cond & G_IO_IN) {
        	read_msinfo_err_io_in(source, cond, data);
	}
	if (cond & G_IO_HUP) {
		if (read_done == 999) {
        		read_done = get_wait_ret(-1);
			dodebug(10,"Finished in read_msinfo_stderr with status: %d\n", 
				read_done);
		}
		return (FALSE);
	}
	return (TRUE);
}


/*
 * call cdrecord -msinfo
 * returns the start sector and the next writeable adress
 * or both values -1 when no offset could be found...
 */
void get_msinfo_info(gint devnr, gint *nr1, gint *nr2) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp3[MAXLINE];
gint read_out, read_err;
GIOChannel *gio, *gio2;

        /* if another msinfo running, ignore */
        if (read_done == 999) {
                return;
        }

        /* mark our msinfo-process as running */
        read_done = 999;
        read_output_ctrl = 0;
        readerr_count = 0;
        cdrecord_reload = 0;
	msinfo_nr1 = -1;
	msinfo_nr2 = -1;

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* build command line */
	get_wrap_path("CDRECORD", tmp3);
	g_snprintf(line,MAXLINE,"%s %s -msinfo",
		tmp3,tmp);

	dodebug(1,"calling: %s\n", line);
	dolog(3, "Executing: %s\n", line);

	readcdda_pid = full_dpl_pipe3(&read_out,&cdrecord_stdin,&read_err,line,1);
	readcdda_pid2 = -1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_msinfo_out, NULL);

	gio2 = g_io_channel_unix_new(read_err);
	g_io_channel_set_flags(gio2, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback2 = g_io_add_watch(gio2, G_IO_IN | G_IO_HUP,
                read_msinfo_stderr, NULL);

        /* now wait until msinfo is finished */
        while (read_done == 999) {
                wait_and_process_events();

                /* check if writer is waiting for reload medium */
                if (cdrecord_reload > 0) {
                        cdrecord_reload = 0;
                        show_dialog(ICO_WARN, _("Please reload the Disc in the writer"), T_OK, NULL, NULL, -1);
                        
                        /* now send CR to cdrecord to let it continue */
                        if (write(cdrecord_stdin, "\n", 1) != 1) {
                                g_warning("write error to cdrecord pipe\n");
                        }
                }

	}

	g_source_remove(readcdda_callback2);
	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);

        close(read_err);
        close(read_out);
	close(cdrecord_stdin);

	dodebug(2,"received msinfo: %d,%d\n", msinfo_nr1, msinfo_nr2);

	/* workaround for drives reporting 0,0 instead of -1,-1 */
	if (msinfo_nr1 == 0 && msinfo_nr2 == 0) {
		msinfo_nr1 = -1;
		msinfo_nr2 = -1;
	}

	*nr1 = msinfo_nr1;
	*nr2 = msinfo_nr2; 
}


/*
 * interpret output of blank=help and sort into memory structure
 */
static void parse_blankmode(gchar *line) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar drv[MAXLINE];
gchar *p1;
gint n;

	if (strncmp(line,"Blanking options:",17) != 0 ) {

		strcpy(tmp,line);
		strip_string(tmp);

		p1=strtok(tmp," \t");
		strcpy(drv,p1);	
		/* now drv contains the driver name */

		p1=strtok(NULL,"");
		strcpy(tmp2,p1);
		strip_string(tmp2);
		/* now tmp2 contains the description */

		/*
		 * next get the description of last mode
		 * to ensure we don't have double modes
		 */
		if (drvcount != 0) {
			if (strcmp(tmp2,blankmodes[drvcount-1]->desc) == 0) {
				/* double drive mode - ignore */
				return;
			}
		}

		/* allocate structure */
		blankmodes[drvcount]=g_new(writer_driver_t,1);

		n = strlen(drv)+1;
		blankmodes[drvcount]->driver=g_new(gchar,n);
		strcpy(blankmodes[drvcount]->driver,drv);

		n = strlen(tmp2)+1;
		blankmodes[drvcount]->desc=g_new(gchar,n);
		strcpy(blankmodes[drvcount]->desc,tmp2);

		drvcount++;
		if (drvcount >= MAXBLANKMODES) {
			g_error("Error: More than %d blank modes found\n",
				MAXBLANKMODES);
		}
	}
}


/*
 * print memory-structure with drivers (debug purposes)
 */
static void print_blankmodes() {
gint count;

	dodebug(2,"--------- cdrecord blankmodes-structure --------\n");
	count = 0;
	while(blankmodes[count] != NULL) {
		dodebug(2,"%s:%s\n",
			blankmodes[count]->driver,
			blankmodes[count]->desc);
			
		count++;
	}
}


/*
 * call cdrecord blank=help
 */
void scanblankmodes() {
gchar line[MAXLINE];
FILE *fpin;

	/* allocate memory */
	blankmodes = g_new0(writer_driver_t *,MAXBLANKMODES);
	drvcount = 0;

	get_wrap_path("CDRECORD", line);
	strcat(line," blank=help 2>&1");

	dodebug(1, "calling: %s\n", line);

        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10, "blankmodes: %s", line);
                parse_blankmode(line);
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	
	if (debug) print_blankmodes();
}


/*
 * open a file and read one sector into a memory-buffer
 * return 1 on success
 */
gint read_info_sector_from_file(gchar *isoname, gchar *buf, gint bsize) {
gint fd;

	dodebug(1, "read iso header from %s\n", isoname);

	fd = open (isoname, O_RDONLY, 0);
	if (fd == -1) {
		return 0;
	}

	/* forward to info-sector */
	if(lseek(fd, 32768, SEEK_SET) < 0) {
		close(fd);
		return 0;
	}

	/* read one sector */
	if (read(fd, buf, bsize) != bsize) {
		close(fd);
		return 0;
	}

	close(fd);
	return 1;
}


/*
 * call readcd to read the info-sector from a data-track into a
 * memory buffer. return 1 if all ok
 */
gint read_info_sector_from_dev(gint devnr, gchar *buf, gint bsize, gint startsector) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar cmd[MAXLINE];
FILE *fpin;
gint s1, s2;

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

        s1 = startsector+16;
        s2 = startsector+17;

	/* build command-line */
	get_wrap_path("READCD", tmp2);
	g_snprintf(cmd,MAXLINE,"%s %s sectors=%d-%d -s f=- 2>/dev/null",
		tmp2, tmp, s1, s2);
	
	dodebug(1, "calling: %s\n", cmd);

	if ((fpin = popen(cmd,"r")) == NULL) {
		g_error("popen error\n");
	}

	/* read into buffer */
	if (fread(buf, bsize, 1, fpin) != 1) {
		pclose(fpin);
		return 0;
	}

	if (pclose(fpin) == -1) {
		g_error("pclose error\n");
	}

	return 1;
}


/*
 * get output of cdrecord -blank
 */
static gboolean read_blank_out_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gint n;

	n = read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

	dodebug(10, "blank (stdout): %s\n", line);

        strip_string(line);
 
         /* look for the reload media command */
         if (strcmp(line,"Re-load disk and hit <CR>") == 0) {
                 cdrecord_reload = 1;
                 return (TRUE);
         }
 
	/* skip all lines that end with "seconds." */
	if (n > 8) {
		if (strstr(line, "seconds.")) {
			return (TRUE);
		}
	}

	strcpy(tmp,line);
	strcat(tmp,"\n");

	append_to_text_view(readtrack_textview, tmp);

	return (TRUE);
}

static gboolean read_blank_out(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	if (cond & G_IO_IN) {
            
            read_blank_out_io_in(source, cond, data);
    	}

	if ((cond & G_IO_IN) && (cond & G_IO_HUP)) {
                read_io_channel_all(source, line, MAXLINE);
		if (strcmp(line, "") == 0) {
			cond = G_IO_HUP;
		} else {
			append_multiline_to_textview(line, "", readtrack_textview, "blank-hup (stdout)");
		}
    	}

	/* finished? */
	if (cond == G_IO_HUP) {

		/* pick up return status of child */
		read_done = get_wait_ret(-1);

		/* perhaps the are-you-sure-you-want-to-abort dialog
		   is still running? remove to be sure we can continue */
		cancel_show_dialog();

                dodebug(10,"Finished in read_blank_out with status: %d\n", 
                        read_done);
		return (FALSE);
	}

    return (TRUE);
}


/*
 * get output of cdrecord -blank (stderr)
 */
static gboolean read_blank_stderr_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gint n;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p;

	n = read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

	dodebug(10, "blank (stderr): %s\n", line);

	/* check if we have an medium-i/o-error */
	strcpy(tmp,line);
	p = strtok(tmp,":");
	if (p != NULL) {
		p = strtok(NULL,"");
		if (p != NULL) {
			strcpy(tmp2,p);
			strip_string(tmp2);
			if (strncmp(tmp2,"I/O error",9) == 0) {
				readerr_count++;
				/* more then once an i/o-error? */
				if (readerr_count > 1) {
					kill_readcdda();
					read_output_ctrl = 2;
				}
			}
		}
	}	

	/* check if we got an error because of no disc */
	if (n > 21) {
		strcpy(tmp, line+(strlen(line)-21));
		if (strcmp(tmp, "No disk / Wrong disk!") == 0) {
			/* mark we've got no disc */
			read_output_ctrl = 2;
		}
	} 	

	strcpy(tmp,line);
	strcat(tmp,"\n");

	append_to_text_view(readtrack_textview, tmp);

	return (TRUE);
}


static gboolean read_blank_stderr(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	if (cond & G_IO_IN) {
        	read_blank_stderr_io_in(source, cond, data);
    	}

	if ((cond & G_IO_IN) && (cond & G_IO_HUP)) {
                read_io_channel_all(source, line, MAXLINE);
		if (strcmp(line, "") == 0) {
			cond = G_IO_HUP;
		} else {
			append_multiline_to_textview(line, "", readtrack_textview, "blank-hup (stderr)");
		}
    	}

	/* finished? */
	if (cond == G_IO_HUP) {
                dodebug(10,"blank (stderr): EOF\n");
		return (FALSE);
    	}

    return (TRUE);
}

/*
 * call cdrecord -blank
 */
gint start_blanking_process(gint devnr, GtkWidget *text_window) {
char tmp[MAXLINE];
char tmp2[MAXLINE];
char tmp3[MAXLINE];
char tmp4[MAXLINE];
char tmp5[MAXLINE];
char tmp6[MAXLINE];
char cmd[MAXLINE];
gint read_out, read_err;
gint ret;
GIOChannel *gio, *gio2;

	/* if another blank running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* mark our blanking-process as running */
	read_done = 999;
	read_output_ctrl = 0;
	readerr_count = 0;
	cdrecord_reload = 0;

	/* save text-window */
	readtrack_textview = text_window;

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* get blankmode and settings */
	strcpy(tmp2, blankmodes[curset.blankmode]->driver);

	strcpy(tmp3,"");
	if (curset.blank_force) {
		strcat(tmp3, "-force ");
	}
	if (curset.blank_eject) {
		strcat(tmp3, "-eject ");
	}
        if (curset.writeignsize == 1) {
                strcat(tmp3,"-ignsize ");
        }
        if (curset.writeimmed == 1) {
                strcat(tmp3,"-immed ");
        }

        if (get_cur_writer_speed() >= 0) {
                g_snprintf(tmp4,MAXLINE,"speed=%d ", get_cur_writer_speed()); 
                strcat(tmp3, tmp4);
        }
	
        get_wrap_path("CDRECORD", tmp5);

	g_snprintf(cmd, MAXLINE,
		"%s %s gracetime=%d -v %sblank=%s", 
		tmp5, tmp, get_gracetime(), tmp3, tmp2);

	dodebug(1, "spawning: %s\n",cmd);
	dolog(1, "Blanking Disc with mode %s\n",tmp2);
	dolog(3, "Executing: %s\n", cmd);

	readcdda_pid = full_dpl_pipe3(&read_out,&cdrecord_stdin,&read_err,cmd,1);
	readcdda_pid2 = -1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_blank_out, NULL);

	gio2 = g_io_channel_unix_new(read_err);
	g_io_channel_set_flags(gio2, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback2 = g_io_add_watch(gio2, G_IO_IN | G_IO_HUP,
                read_blank_stderr, NULL);

	/* disallow delete button press */
	set_dialog_disallow_delete();

	/* now wait until blank is finished */
	while (read_done == 999) {
		wait_and_process_events();

                /* check if writer is waiting for reload medium */
                if (cdrecord_reload > 0) {
                        cdrecord_reload = 0;
                        show_dialog(ICO_WARN, _("Please reload the Disc in the writer"), T_OK, NULL, NULL, -1);
                        
                        /* now send CR to cdrecord to let it continue */
                        if (write(cdrecord_stdin, "\n", 1) != 1) {
                                g_warning("write error to cdrecord pipe\n");
                        }
                }

		/* check if somebody clicked on cancel */
		if (get_dialog_delete_pressed()) {
			clear_dialog_delete_pressed();
			ret = show_dialog(ICO_WARN, _("Are you sure you want to abort the writing?\nAborting may damage the Disc and may require that you have\nto power cycle the writer to get it working again."),
				T_YES, T_NO, NULL, 1);
			if (ret == 0) {
			 	/* really abort */
				kill_readcdda();
				/* mark we aborted */
				read_output_ctrl = 1;
			} else {
				/* not abort - continue */
				;
			}
		}
	}

	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);

	close(read_err);
	close(read_out);
	close(cdrecord_stdin);

	set_dialog_allow_delete();
	cancel_show_dialog();

	/* no disc */
	if (read_output_ctrl == 2) {
		append_to_text_view(readtrack_textview, "\n");

		strncpy(tmp6, _("No Disc or wrong Disc loaded."), MAXLINE);
		append_to_text_view(readtrack_textview, tmp6);
		append_to_text_view(readtrack_textview, "\n\n");
	}

	/* error while blanking */
	if (read_done != 0) {
		return 1;
	}

	/* we killed the process */
	if (read_output_ctrl == 1) {
		return 2;
	}

	return 0; 
}


/*
 * a timer function to see if there is output from cdrecord
 * within a given time
 */
static gint cdrtimer_callback(gpointer data) {
time_t acttime;
gint diff;

	/* how many time passed since the last output of cdrecord? */
	acttime = time((time_t *) 0);
	diff = (gint) (acttime - cdrcmdtimer);	
	
	/* we counting down seconds, but not receiving output since over 5? */
	if (diff > 5) {
		g_source_remove(cdrtimer);
		cdrtimer = 0;

		dodebug(10,"cdrecord.prodvd output-timer run out - assume termination.\n");
		/* force termination of cdrecord.prodvd */
		read_write_out(NULL, G_IO_IN, NULL);

		/* if mkisofs is marked as running, kill it too */
		if (mkisofs_pid != -1) {
			dodebug(2,"sending SIGINT to mkisofs (pid %d)\n", 
				(gint) mkisofs_pid);
			kill(mkisofs_pid, SIGINT);
			get_wait_ret(mkisofs_pid);
		}

		return(FALSE);
	}

    	return(TRUE);
}


/*
 * get output of cdrecord
 */
static gboolean read_write_out_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gint n;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p, *p2;
gint tnr, donemb, fullmb, fifoval, intbufval;
gfloat pval, pval2, pval3, pval4, avgspd;

	if (source != NULL) {
		n = read_io_channel(source, line, MAXLINE);
	} else {
		/* force finish */
		n = -1;
	}

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		read_write_idle_loop((pid_t)GPOINTER_TO_INT(data));
		return (TRUE);
	}

	dodebug(10,"cdrecord (stdout): %s\n", line);

	cdrcmdtimer = time((time_t *) 0);

	strip_string(line);

	/* look for the reload media command */
	if (strcmp(line,"Re-load disk and hit <CR>") == 0) {
		cdrecord_reload = 1;
		return (TRUE);
	}

	/* cdrecord will wait for SIGUSR1 while waiting for reload? */
	if (strstr(line, "send SIGUSR1 to continue")) {
                cdrecord_reload = 2;
		return (TRUE);
	}

	/* if (curset.isProDVD && curset.cdrtype > 1000) { *** obsolete ***/
	if (get_gracetime() > 4) {
		if (strstr(line,"Last chance to quit")) {
			cdrtimer = g_timeout_add(1000, 
					cdrtimer_callback, NULL);
		}

		if (strstr(line,"1 second")) {
			if (cdrtimer) {
				g_source_remove(cdrtimer);
				cdrtimer = 0;
			}
		}
	}

	/* skip all lines that contain "seconds." */
	if (n > 8) {
		if (strstr(line, "seconds.")) {
			/* mark we are now ready to rumble */
			read_output_ctrl = 3;

			/* starting the write here */
			writecompleted = 0;
			return (TRUE);
		}
	}

	/* track lines */
	if (read_output_ctrl > 0 && strncmp(line,"Track",5) == 0 &&
		!strstr(line,"audio") && !strstr(line,"data")) {

		/* parser */
		avgspd = 0.0;
		strcpy(tmp, line+6);
		p = strtok(tmp,":");
		if (p == NULL) return (TRUE);
		tnr = atoi(p);
		p = strtok(NULL," ");
		if (p == NULL) return (TRUE);
		if ((strncmp(p,"Total",5) != 0) && 
		    (strncmp(p,"writing",7) != 0)) {
			/* not the Total bytes written line? */
			donemb = atoi(p);

			p = strtok(NULL," ");
			p = strtok(NULL," ");
			if (p == NULL) return (TRUE);
			fullmb = atoi(p);

			p = strtok(NULL,"%");
			p2 = strtok(NULL,"");
			if (p2 == NULL) {
				/* no fifo value available - start of track */
				fifoval = 0;
				intbufval = 0;
				avgspd = 0.0;
				if (writeparams.tracktype[tnr] == 0) {
					/* data track */
					convert_frames2mbstring(
						writeparams.frames[tnr],tmp);
					g_snprintf(tmp2,MAXLINE, _("Writing data track %d/%d [%s]"),
						tnr, 
						writeparams.nrtracks, tmp);
					if (writeparams.simulation) 
						strcat(tmp2,_(" [Simulation]"));
					gtk_label_set_text(
						GTK_LABEL(readtrack_info_label), 
						tmp2);
				} else {
					/* audio track */
					convert_frames2minstring(
						writeparams.frames[tnr],tmp);
					g_snprintf(tmp2,MAXLINE, _("Writing audio track %d/%d [%s]"),
						tnr, 
						writeparams.nrtracks, tmp);
					if (writeparams.simulation) 
						strcat(tmp2,_(" [Simulation]"));
					gtk_label_set_text(
						GTK_LABEL(readtrack_info_label), 
						tmp2);
				}
				/* small view label */ 
				g_snprintf(tmp2,MAXLINE, _("Writing %d/%d:"),
					tnr, writeparams.nrtracks);
				gtk_label_set_text(
					GTK_LABEL(readtrack_small_info),tmp2);
			} else {
				/* extract fifo value */
				strcpy(tmp2,p);
				p = rindex(tmp2,' ');
				if (p == NULL) return (TRUE);
				fifoval = atoi(p);
				
				/* extract buf/spd value */
				strcpy(tmp2,p2);
				p = strtok(tmp2,"%");
				if (p) {
					p2 = strtok(NULL,"");
					strcpy(tmp,p);
					p = rindex(tmp,' ');	
					if (p == NULL) return (TRUE);
					if (strstr(tmp, "buf")) {
						intbufval = atoi(p);
					} else {
						/* no buf value? speed next */
						intbufval = 0;
						strcpy(tmp2,tmp);
						p2 = tmp2;
					}
					if (p2) {
						strcpy(tmp,p2);
						p = strtok(tmp,"x");
						if (p) {
							strcpy(tmp2,p);
							p = rindex(tmp2,' ');	
							if (p == NULL) return (TRUE);
							avgspd = (gfloat)atof(p);
						}
					}
				} else {
					intbufval = 0;
					avgspd = 0.0;
				}
			}
			/* done with parsing */

			/* calculate current track progressbar values */
			if (fullmb != 0) {
				pval = (gfloat)donemb/(gfloat)fullmb;
				if (pval > 1.0) pval = 1.0;

				gtk_progress_set_percentage(
					GTK_PROGRESS(readtrack_pbar1), pval);
			} else {
				pval = 0.0;
			}

			/* calculate total track progressbar */
			pval2 = (writeparams.pct_so_far_arr[tnr] + 
				(pval * writeparams.pct_this_track_arr[tnr]));
			/* go only up to 99% */
			if (pval2 >= 0.99) pval2 = 0.99;

			if (writeparams.nrtracks != 1) {
				gtk_progress_set_percentage(
					GTK_PROGRESS(readtrack_pbar2), pval2);
				set_xcdr_title(toplevel,viewmode_dialog,(gint)(pval2*100+0.5));
			} else {
				/* just one track? total is the same as track */
				if (pval < 1.0) {
					gtk_progress_set_percentage(
						GTK_PROGRESS(readtrack_pbar2), 
						pval);
					set_xcdr_title(toplevel,viewmode_dialog,(gint)(pval*100+0.5));
				} else {
					gtk_progress_set_percentage(
						GTK_PROGRESS(readtrack_pbar2), 
						0.99);
					set_xcdr_title(toplevel,viewmode_dialog,99);
				}
			}	

			/* fifo value */
			pval3 = (gfloat)fifoval /100;	
			if (pval3 > 1.0) pval3 = 1.0;
			gtk_progress_set_percentage(
				GTK_PROGRESS(readtrack_pbar3), pval3);

			/* add internal buffer and average speed too */
			pval4 = (gfloat)intbufval /100;	
			if (pval4 > 1.0) pval4 = 1.0;
			gtk_progress_set_percentage(
				GTK_PROGRESS(readtrack_pbar4), pval4);

			if (avgspd > 0.0) {
				g_snprintf(tmp,MAXLINE,"%.1fx",avgspd);
				gtk_label_set_text(GTK_LABEL(readtrack_spd), tmp);
			}

			/* now update info for small view */
			g_snprintf(tmp,MAXLINE,"%d%% / %d%%",
				(gint)(pval*100+0.5), (gint)(pval2*100+0.5));
			gtk_label_set_text(
				GTK_LABEL(readtrack_small_info2),tmp);	

			return (TRUE);
		}
	}

	/* fixating */
	if (strncmp(line,"Fixating...",11) == 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Fixating..."));
		/* ok, write is finished I guess */
		writecompleted = 1;
	}

	/* avg speed line */
	if (strncmp(line,"Average write speed", 19) == 0) {
		strcpy(tmp,line);
		p = strtok(tmp,"x");
		if (p) {
			strcpy(tmp2,p);
			p = rindex(tmp2,' ');	
			if (p) {
				avgspd = (gfloat)atof(p);
				g_snprintf(tmp,MAXLINE,"%.1fx",avgspd);
				gtk_label_set_text(GTK_LABEL(readtrack_spd), tmp);
			}
		}
	}

	strcpy(tmp,line);
	strcat(tmp,"\n");

	append_to_text_view(readtrack_textview, tmp);

	return (TRUE);
}

static gboolean read_write_out_io_hup(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar tmp[MAXLINE];
gint read_done_new;

		/* pick up return status of child */
		read_done_new = get_wait_ret(readcdda_pid);
		if (read_done == 999) {
			/* save status only once (this can be called twice) */
			read_done = read_done_new;
		} else {
			/* skip second call */
			return (FALSE);
		}
	
		if (writecompleted == 1 && read_done == 0) {	
			/* set total progress bar to 100% */
			gtk_progress_set_percentage(
				GTK_PROGRESS(readtrack_pbar2), 1.0);
			set_xcdr_title(toplevel,viewmode_dialog,100);

			/* and small view too */
			g_snprintf(tmp,MAXLINE,"%d%% / %d%%",100,100);
			gtk_label_set_text(
				GTK_LABEL(readtrack_small_info2),tmp);	
		}

		/* perhaps the are-you-sure-you-want-to-abort dialog
		   is still running? remove to be sure we can continue */
		cancel_show_dialog();

                dodebug(10,"Finished in read_write_out with status: %d\n", 
                        read_done);

		return (FALSE);
}

static gboolean read_write_out(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	if (cond & G_IO_IN) {
        	read_write_out_io_in(source, cond, data);
    	}

	if ((cond & G_IO_IN) && (cond & G_IO_HUP)) {
                read_io_channel_all(source, line, MAXLINE);
		if (strcmp(line, "") == 0) {
			cond = G_IO_HUP;
		} else {
			append_multiline_to_textview(line, "", readtrack_textview, "cdrecord-hup (stdout)");
		}
    	}

	/* finished? */
	if (cond == G_IO_HUP) {
		dodebug(10,"cdrecord (stdout): EOF\n");
		return(read_write_out_io_hup(source, cond, data));
    	}

    return (TRUE);
}


static gboolean read_write_stderr_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gint n;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p;

	n = read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

	dodebug(10,"cdrecord (stderr): %s\n", line);

	/* check if we have an medium-i/o-error */
	strcpy(tmp,line);
	p = strtok(tmp,":");
	if (p != NULL) {
		p = strtok(NULL,"");
		if (p != NULL) {
			strcpy(tmp2,p);
			strip_string(tmp2);
			if (strncmp(tmp2,"I/O error",9) == 0) {
				readerr_count++;
				/* more then once an i/o-error? */
				if (readerr_count > 1) {
					kill_readcdda();
					read_output_ctrl = 2;
				}
			}
			/* check also if cannot send CUE sheet */
			if (strncmp(tmp2,"Cannot send CUE sheet", 21) == 0) {
				read_output_ctrl = 4;
			}
			/* check for common error on TAO only devices */
			if (strncmp(tmp2,"Cannot open new session.",24) == 0 && get_cur_writemode() == 0) {
				read_output_ctrl = 4;
			} 
			/* check Medium error/buffer underrun */
			if (strncmp(tmp2,"Input/output error",18) == 0) {
				read_output_ctrl = 5;
			}
		}
	}	


	/* check if we got an error because of no disc */
	if (n > 21) {
		strcpy(tmp, line+(strlen(line)-21));
		if (strcmp(tmp, "No disk / Wrong disk!") == 0) {
			/* mark we got no disc */
			read_output_ctrl = 2;
		}
	} 	

	strcpy(tmp,line);
	strcat(tmp,"\n");

	append_to_text_view(readtrack_textview, tmp);

	return (TRUE);
}

static gboolean read_write_stderr(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	if (cond & G_IO_IN) {
        	read_write_stderr_io_in(source, cond, data);
    	}

	if ((cond & G_IO_IN) && (cond & G_IO_HUP)) {
                read_io_channel_all(source, line, MAXLINE);
		if (strcmp(line, "") == 0) {
			cond = G_IO_HUP;
		} else {
			append_multiline_to_textview(line, "", readtrack_textview, "cdrecord-hup (stderr)");
		}
    	}

	/* finished? */
	if (cond == G_IO_HUP) {
		dodebug(10,"cdrecord (stderr): EOF\n");
		return(read_write_out_io_hup(source, cond, data));
    	}

    return (TRUE);
}

static void read_write_idle_loop(pid_t cdrecord_pid) {
gint ret;
		/* check if writer is waiting for reload medium */
		if (cdrecord_reload > 0) {
			show_dialog(ICO_WARN, _("Please reload the Disc in the writer"), T_OK, NULL, NULL, -1);
			
			/* now send CR to cdrecord to let it continue */
			if (cdrecord_reload == 1) {
				if (write(cdrecord_stdin, "\n", 1) != 1) {
					g_warning("write error to cdrecord pipe\n");
				}
			}

			/* cdrecord also accepts a SIGUSR1 to continue
			   send wakeup signal */
			if (cdrecord_reload == 2 && cdrecord_pid != 0) {
				dodebug(1,"Sending SIGUSR1 to cdrecord (pid: %d) after reload.\n", (gint) cdrecord_pid);
				kill(cdrecord_pid, SIGUSR1);
			}
			cdrecord_reload = 0;
		}

		/* check if somebody clicked on cancel */
		if (get_dialog_delete_pressed()) {
			clear_dialog_delete_pressed();
			ret = show_dialog(ICO_WARN, _("Are you sure you want to abort the writing?\nAborting may damage the Disc and may require that you have\nto power cycle the writer to get it working again."),
				T_YES, T_NO, NULL, 1);
			if (ret == 0) {
				/* really abort */
				kill_readcdda();
				/* mark we aborted */
				read_output_ctrl = 1;	
			} else {
				/* not abort - continue */
				;
			}
		}
}

/*
 * call cdrecord to write some tracks
 */
gint start_write_action(gint devnr) {
GList *loop;
track_read_param_t *trackparam;
gchar tmp[MAXLINE];
gchar tmptmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gchar cmd[MAXLINE*10];  /* extra big buffer for very long cdrecord options */
gchar outcmd[MAXLINE];
gint read_out, read_err;
gint tracknr;
gint varirec_on, tmp_writer_speed;
gint wrmode;
GIOChannel *gio, *gio2;

	/* if another write running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* mark our write-process as running */
	read_done = 999;
	read_output_ctrl = 0;
	pct_so_far = 0.0;
	cdrecord_reload = 0;
	cdrtimer = 0;
	
	strcpy(outcmd,"");

	/* set info label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Initializing Disc Writer..."));

	/* reset writeparams-arrays */
	g_free(writeparams.tracktype);
	g_free(writeparams.frames);
	g_free(writeparams.pct_so_far_arr);
	g_free(writeparams.pct_this_track_arr);
	writeparams.tracktype = g_new0(gint,MAXTRACKS);
	writeparams.frames = g_new0(gint,MAXTRACKS);
	writeparams.pct_so_far_arr = g_new0(gfloat,MAXTRACKS);
	writeparams.pct_this_track_arr = g_new0(gfloat,MAXTRACKS);
	writeparams.nrtracks = 0;
	writeparams.simulation = curset.writesimul;

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}
	
	/* get driveropts */
	varirec_on = do_driveropts(tmp3, curset.writer_devnr);

	/* build command line */
	get_wrap_path("CDRECORD", tmp2);
	g_snprintf(cmd, MAXLINE*10,
		"%s %s gracetime=%d fs=%dm %s -v -useinfo", 
		tmp2, tmp, get_gracetime(), get_cur_writer_fifo(), tmp3);

	if (get_cur_writer_drvmode() >= 0) {
		if (drivers[get_cur_writer_drvmode()] != NULL) {
			g_snprintf(tmp,MAXLINE," driver=%s", 
				drivers[get_cur_writer_drvmode()]->driver);
			strcat(cmd,tmp);
		}
	}
	
	/* special case with varirec - always use 4x here */
	if (varirec_on) {
		tmp_writer_speed = 4;
	} else {
		tmp_writer_speed = get_cur_writer_speed();
	}
	if (get_cur_writer_speed() >= 0) {
		g_snprintf(tmp,MAXLINE," speed=%d", tmp_writer_speed);	
		strcat(cmd, tmp);
	}

	/*
	 * allow setting of write mode only for CD
	 * as for DVD and BD solely DAO is possible
	 */
	wrmode = get_cur_writemode();
	if (curset.cdrtype < 1000 && !curset.multisession) {
		if (wrmode == 0) {
			strcat(cmd," -dao");
		}
		if (wrmode == 1) {
			strcat(cmd," -tao");
		}
		if (wrmode == 2) {
			strcat(cmd," -tao defpregap=0");
		}
		if (wrmode == 3) {
			strcat(cmd," -raw96r");
		}
		if (wrmode == 4) {
			strcat(cmd," -raw96p");
		}
		if (wrmode == 5) {
			strcat(cmd," -raw16");
		}
	} else if (curset.cdrtype < 1000 && curset.multisession) {
		strcat(cmd," -tao");
	} else {
		strcat(cmd," -dao");
	}
	if (curset.multisession) {
		if (is_a_sony(devnr)) {
			strcat(cmd," -multi -data");
		} else {
			strcat(cmd," -multi");
		}
	}
	if (curset.writesimul) {
		strcat(cmd," -dummy");
	}
	if (curset.writeeject) {
		strcat(cmd," -eject");
	}
	/*
	 * multisession data disc must be fixated,
	 */
	if (curset.nofixate && !curset.multisession) {
		strcat(cmd," -nofix");
	}
	if (curset.writepad) {
		strcat(cmd," -pad");
	}
	if (curset.writeswap) {
		strcat(cmd," -swab");
	}
	if (curset.writeoverburn) {
		strcat(cmd," -overburn");
	}
	if (curset.writeignsize) {
		strcat(cmd," -ignsize");
	}
	if (curset.writeimmed) {
		strcat(cmd," -immed");
	}
	/*
	 * CD text can only be written in
	 * mode DAO or raw96r or raw96p
	 */
	if (curset.writecdtext && (wrmode == 0 || wrmode == 3 || wrmode == 4)) {
		strcat(cmd," -text");
	}

	/* now loop through all tracks to write */
	loop = g_list_first(trackreadset.trackparams);
	while(loop) {
		trackparam = loop->data;

		if (trackparam->tracktype == 0) {
			/* datatrack */
			strcat(cmd," -data");
		} else {
			/* audiotrack */
			strcat(cmd," -audio");
		}
		if (trackparam->trackfile != NULL) {
			strcpy(tmptmp, trackparam->trackfile);
			g_snprintf(tmp, MAXLINE, " \"%s\"",
				convert_escape(tmptmp));
			strcat(cmd, tmp);
		}

		/* save partial command line to view in progress window */
		if (strcmp(outcmd,"") == 0) {
			g_snprintf(outcmd, MAXLINE,"Calling: %s ...\n\n", cmd);
		}

		/* fill up percent values for nice progressbar movement */
		tracknr = trackparam->starttrack;
		writeparams.tracktype[tracknr] = trackparam->tracktype;
		writeparams.frames[tracknr] = trackparam->frames;
		writeparams.pct_this_track_arr[tracknr] = trackparam->percent;	
		writeparams.pct_so_far_arr[tracknr] = pct_so_far;
		pct_so_far += trackparam->percent;
		writeparams.nrtracks++;

		loop = loop->next;
	}

	dodebug(1, "spawning: %s\n",cmd);
	dolog(1,"Write Disc with %d tracks\n", writeparams.nrtracks);
	dolog(3,"Executing: %s\n", cmd);

	/* output command to get better debug output */
	append_to_text_view(readtrack_textview, outcmd);

	/* yes...command line is done */
	readcdda_pid = full_dpl_pipe3(&read_out,&cdrecord_stdin,&read_err,cmd,1);
	readcdda_pid2 = -1;
	mkisofs_pid = -1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_write_out, NULL);

	gio2 = g_io_channel_unix_new(read_err);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback2 = g_io_add_watch(gio2, G_IO_IN | G_IO_HUP,
                read_write_stderr, NULL);

	/* disallow delete button press */
	set_dialog_disallow_delete();

	/* now wait until write is finished */
	while (read_done == 999) {
		wait_and_process_events();

		read_write_idle_loop((pid_t)0);
	}

	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);

	close(read_err);
	close(read_out);
	close(cdrecord_stdin);

	set_dialog_allow_delete();
	cancel_show_dialog();

	/* no disc */
	if (read_output_ctrl == 2) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("No Disc or wrong Disc loaded."));
		return 1;
	}
	/* cannot send cue sheet */
	if (read_output_ctrl == 4) {
		show_dialog(ICO_WARN,_("Your CD-Writer did not accept the CUE sheet needed for\nDAO writing. This could mean that cdrecord does not support DAO\nwith your writer. Select TAO mode and try again."),T_OK, NULL, NULL, 0);
	}

	/* aborted write ? */
	if (read_output_ctrl == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Writing aborted..."));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Write-Error:"));
		return 1;
	}

	/* error while writing ?*/
	if (read_done != 0 || read_output_ctrl == 5) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Error writing tracks"));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Write-Error:"));
		return 1;
	} else {
		/* all ok */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Tracks successfully written"));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Successful:"));
		return 0;
	}
}


/*
 * call cdda2wav to only create temporary .inf files - and let xcdroast
 * patch CD-Text data into them if requested
 */
static void create_onthefly_inf_files(gint devnr) {
GList *loop;
track_read_param_t *trackparam;
char tmp[MAXLINE];
char tmptmp[MAXLINE];
char tmp4[MAXLINE];
char tmp5[MAXLINE];
char tmp8[MAXLINE];
char line[MAXLINE];
char cmd[MAXLINE*10];  /* extra big buffer for very long cdrecord options */
FILE *fpin;
gint ret, i, orgcdtextused;
gchar title[MAXLINE], artist[MAXLINE];
gchar cdtitle[MAXLINE], cdartist[MAXLINE];

        /* get bus,id,lun string */
        if (convert_devnr2busid(devnr,tmp) != 0) {
                g_error("non existing cdrom?");
        }

        /* have to scan for indexes? */
        if (curset.indexscan) {
                strcpy(tmp4,"-v all");
        } else {
                strcpy(tmp4,"-v toc,summary,sectors,titles");
        }

	/* have to enable -no-hidden-track? */
	if (get_cur_audioread_showhiddentrack()) {
		strcpy(tmp8,"");
	} else {
		strcpy(tmp8,"-no-hidden-track");
	}

	get_wrap_path("CDDA2WAV", tmp5);
	g_snprintf(cmd,MAXLINE,
		   "%s -D \"%s\" -g -info-only %s %s -B",
		   tmp5,tmp,tmp4,tmp8);

        /* now add all track-filenames (only audio) */
        loop = g_list_first(trackreadset.trackparams);
        while(loop) {
                trackparam = loop->data;
                if (trackparam->trackfile != NULL && 
                    trackparam->tracktype == 1) {
                        strcpy(tmptmp, trackparam->trackfile);
                        g_snprintf(tmp, MAXLINE, " \"%s\"",
                                convert_escape(tmptmp));
                        strcat(cmd, tmp);
		}
                loop = loop->next;
        }
	strcat(cmd, " 2>&1");

        dodebug(1,"calling: %s\n", cmd);
        if ((fpin = popen(cmd,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
                dodebug(10,"cdda2wav_info_only: %s", line);
		wait_and_process_events();
        }

        ret = pclose(fpin);
        if (ret == -1) {
                g_error("pclose error\n");
        }

	/* info files now (hopefully) created */

	/* check for invalid mcn or isrc infoormation */
        loop = g_list_first(trackreadset.trackparams);
        while(loop) {
                trackparam = loop->data;
                if (trackparam->trackfile != NULL && 
                    trackparam->tracktype == 1) {

			/* silently fix any broken .inf file */
			if (check_valid_isrc_mcn(trackparam->trackfile)) {
				clear_isrc_mcn_from_inffile(trackparam->trackfile);
			}
		}
		loop = loop->next;
	}

	/* patch updated cd-text info */
	if (curset.writecdtext) {

		/* get title and artist for the given CD */
	        strcpy(cdtitle,""); 
        	strcpy(cdartist,"");
		orgcdtextused = 0;
                if (setupdata.option_displaycdtext) {
                       if (cdinfo.title && cdinfo.artist && cdinfo.title[0] != '\0') {
                                strncpy(cdtitle, cdinfo.title, MAXLINE);
                                strncpy(cdartist, cdinfo.artist, MAXLINE);
                                orgcdtextused = 1;
                        }
                }
                if (orgcdtextused == 0) {
                        if (cdinfo.cddb_dtitle) {
                                get_artist_and_title_from_cddb(cdinfo.cddb_dtitle,
                                cdartist, cdtitle);
                        } else {
                                /* try cd-text as fallback */
                                if (cdinfo.title && cdinfo.artist &&
                                    cdinfo.title[0] != '\0') {
                                        strncpy(cdtitle, cdinfo.title, MAXLINE);
                                        strncpy(cdartist, cdinfo.artist, MAXLINE);                                }
                        }
                }
		
/*
		g_print("cdtitle/artist %s %s\n", cdtitle, cdartist);
*/
        	loop = g_list_first(trackreadset.trackparams);
	        while(loop) {
        	        trackparam = loop->data;
	                if (trackparam->trackfile != NULL && 
                	    trackparam->tracktype == 1) {

				/* get the cd-text data */
				i = trackparam->trackinfo_index;
		                strcpy(title,"");
              		  	strcpy(artist,"");
               	 		orgcdtextused = 0;
                		if (setupdata.option_displaycdtext) {
                        		if (trackinfo[i]->title != NULL &&
                            		    trackinfo[i]->title[0] != '\0' ) {
                                		/* cd-text found.. use it */
                                		strncpy(title, trackinfo[i]->title, MAXLINE);
                                		orgcdtextused = 1;
                        		}
                        		if (trackinfo[i]->artist != NULL &&
                            			trackinfo[i]->artist[0] != '\0' ) {
                                		strncpy(artist, trackinfo[i]->artist, MAXLINE);
                        		}
                		}
		                /* no cd-text in use? try cddb */
		                if (orgcdtextused == 0) {
                        		if (trackinfo[i]->cddb_ttitle != NULL) {
                                		/* use cddb-information */
                                		strncpy(title, trackinfo[i]->cddb_ttitle,
                                        		MAXLINE);
                        		} else {
                                		/* fall back to try cd-text again */
                                		if (trackinfo[i]->title != NULL &&
                                    		trackinfo[i]->title[0] != '\0' ) {
                                        		strncpy(title, trackinfo[i]->title, MAXLINE);
                                		}
                                		if (trackinfo[i]->artist != NULL &&
                                    		    trackinfo[i]->artist[0] != '\0' ) {
                                        		strncpy(artist, trackinfo[i]->artist, MAXLINE);
                                		}
                        		}	
                		}

				/* now we have all cd-text info for the given track */
				edit_xinf_for_cd_text2(trackparam->trackfile, title, artist, cdtitle, cdartist);
			 
			}	
			loop = loop->next;
		}
	}
}


/*
 * after the on-the-fly write remove the temporary-inf files again
 */
static void remove_onthefly_inf_files() {
GList *loop;
track_read_param_t *trackparam;
char tmptmp[MAXLINE];

        loop = g_list_first(trackreadset.trackparams);
        while(loop) {
                trackparam = loop->data;
                if (trackparam->trackfile != NULL && 
                    trackparam->tracktype == 1) {
                        strcpy(tmptmp, trackparam->trackfile);
			remove_tmp_writetracks_tocfile(tmptmp);
		}
                loop = loop->next;
        }
}


/*
 * call cdrecord to write a disc on the fly
 * works currently only for discs with a single data-track
 * and pure audio CDs
 */ 
gint start_write_onthefly_action(gint read_devnr, gint write_devnr) {
GList *loop;
track_read_param_t *trackparam;
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gchar tmp4[MAXLINE];
gchar tmp8[MAXLINE];
gchar atmp3[MAXLINE];
gchar tmptmp[MAXLINE];
gchar atmp6[MAXLINE];
gchar cmd[MAXLINE*10];  /* extra big buffer for very long cdrecord options */
gchar cmd2[MAXLINE*10];
gchar outcmd[MAXLINE];
gchar tmpprefix[MAXLINE];
gint read_out, read_err, read_err2;
gint tracknr, i;
pid_t pid1, pid2;
gint endsector, sectsize;
gint varirec_on, tmp_writer_speed;
gint wrmode;
gint datacount, audiocount;
gint isaudiocd;
gfloat per;
GIOChannel *gio, *gio2, *gio3;

	/* if another write running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* what we got to do here? */
	datacount = 0;
	audiocount = 0;
	isaudiocd = 0;
        for (i = 0; i < cdinfo.nr_tracks; i++) {
                if (trackinfo[i]->type == 0) { 
                        datacount++;
                } else {
                        audiocount++;
                }
        }
	if (datacount == 0 && audiocount > 0) {
		isaudiocd = 1;
	}
	/* special case - cd extra? */
	if (datacount == 1 && audiocount > 0 && trackinfo[0]->type == 1) {
		/* treat just like an audio cd - ignore data track */
		isaudiocd = 1;
	}
	
	/* fetch a prefix we might need */
	generate_tmp_prefix_name(tmpprefix);

	/*
	 * audio CD on the fly -
	 * rebuild our structure
	 * for the tracks to read
	 */
	if (isaudiocd) {

		clear_trackreadset();
		trackreadset.nrtracks = cdinfo.nr_tracks;

		/* create a list of temporary track files */
		for(i=0; i<cdinfo.nr_tracks; i++) {

			trackparam = g_new0(track_read_param_t,1);
			trackparam->trackinfo_index = i;
			trackparam->starttrack = trackinfo[i]->track_nr;
			trackparam->endtrack = 0;
			trackparam->tracktype = trackinfo[i]->type;
			per = (gfloat)trackinfo[i]->size * 100.0  / cdinfo.total_size;
			trackparam->percent = per/100.0;
			trackparam->frames = trackinfo[i]->size;

			g_snprintf(tmp,MAXLINE, "%s-%02d.inf",
				tmpprefix, i);
			trackparam->trackfile = g_strdup(tmp);

			trackreadset.trackparams = g_list_append(
				trackreadset.trackparams, trackparam);
		}

		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Scanning audio tracks for indices and ISRC..."));
		create_onthefly_inf_files(read_devnr);
	} 

	/* mark our write-process as running */
	read_done = 999;
	read_output_ctrl = 0;
	pct_so_far = 0.0;
	cdrecord_reload = 0;
	cdrtimer = 0;
	endsector = 0;
	sectsize = 0;
	strcpy(outcmd,"");

	/* set info label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Initializing Disc Writer..."));

	/* reset writeparams-arrays */
	g_free(writeparams.tracktype);
	g_free(writeparams.frames);
	g_free(writeparams.pct_so_far_arr);
	g_free(writeparams.pct_this_track_arr);
	writeparams.tracktype = g_new0(gint,MAXTRACKS);
	writeparams.frames = g_new0(gint,MAXTRACKS);
	writeparams.pct_so_far_arr = g_new0(gfloat,MAXTRACKS);
	writeparams.pct_this_track_arr = g_new0(gfloat,MAXTRACKS);
	writeparams.nrtracks = 0;
	writeparams.simulation = curset.writesimul;


	/*
	 * build command line of readprocess *
	 */
	if (!isaudiocd) { /* data disc */

		/* get bus,id,lun string */
		if (convert_devnr2busid_dev(read_devnr,tmp) != 0) {
			g_error("non existing cdrom?");
		}
		/* because we have only a single track on the disc
		   this is our endsector to read */
		endsector = cdinfo.leadout - 2;

	        sectsize = get_sectorsize(read_devnr);
		if (sectsize == 2352) {
			/* workaround for buggy drives */
	       	 	endsector = ((endsector * DATASECTORSIZE) / sectsize) + 1;
		}

		get_wrap_path("READCD", tmp2);
		g_snprintf(cmd,MAXLINE,
			"%s %s sectors=0-%d -s f=-",
			tmp2, tmp, endsector);

	} else { /* audio CD */

	        /* get bus,id,lun string */
	        if (convert_devnr2busid(read_devnr,tmp) != 0) {
       	         	g_error("non existing cdrom?");
        	}

	        /* set speed only when not zero */
       	 	if (get_cur_audioread_speed() > 0) {
                	g_snprintf(atmp3,MAXLINE,"-S %d", get_cur_audioread_speed());         
		} else {
                	strcpy(atmp3,"");
        	}

		/* set paranoia options */
		if (get_cur_audioread_useparanoia() && (get_cur_audioread_paranoiaminoverlap() < 1)) {
			g_snprintf(atmp6, MAXLINE, "paraopts=retries=%d,readahead=%d",
				   get_cur_audioread_paranoiaretries(),get_cur_audioread_paranoiareadahead());
		} else if (get_cur_audioread_useparanoia()) {
			g_snprintf(atmp6, MAXLINE, "paraopts=retries=%d,readahead=%d,minoverlap=%d",
				   get_cur_audioread_paranoiaretries(),get_cur_audioread_paranoiareadahead(),get_cur_audioread_paranoiaminoverlap());
        	} else {
                	g_snprintf(atmp6, MAXLINE, "-P %d", get_cur_audioread_overlap());
        	}

		/* have to enable -no-hidden-track? */
		if (get_cur_audioread_showhiddentrack()) {
			strcpy(tmp8,"");
		} else {
			strcpy(tmp8,"-no-hidden-track");
		}

	        get_wrap_path("CDDA2WAV", tmp2);
	        g_snprintf(cmd,MAXLINE,
			   "%s -D \"%s\" -g -O raw %s -no-infofile -v disable %s %s -B -",
			   tmp2,tmp,atmp3,atmp6,tmp8);
	}


	/*
	 * build command line of write process *
	 */

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(write_devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* get driveropts */
	varirec_on = do_driveropts(tmp4, curset.writer_devnr);

	get_wrap_path("CDRECORD", tmp3);
	g_snprintf(cmd2, MAXLINE*10,
		"%s %s gracetime=%d fs=%dm %s -v -useinfo",  
		tmp3, tmp, get_gracetime(), get_cur_writer_fifo(), tmp4);

	if (get_cur_writer_drvmode() >= 0) {
		if (drivers[get_cur_writer_drvmode()] != NULL) {
			g_snprintf(tmp,MAXLINE," driver=%s", 
				drivers[get_cur_writer_drvmode()]->driver);
			strcat(cmd2,tmp);
		}
	}
	
	/* special case with varirec - always use 4x here */
	if (varirec_on) {
		tmp_writer_speed = 4;
	} else {
		tmp_writer_speed = get_cur_writer_speed();
	}
	if (get_cur_writer_speed() >= 0) {
		g_snprintf(tmp,MAXLINE," speed=%d", tmp_writer_speed);	
		strcat(cmd2, tmp);
	}

	/*
	 * allow setting of write mode only for CD
	 * as for DVD and BD solely DAO is possible
	 */
	wrmode = get_cur_writemode();
	if (curset.cdrtype < 1000) {
		if (wrmode == 0) {
			strcat(cmd2," -dao");
		}
		if (wrmode == 1) {
			strcat(cmd2," -tao");
		}
		if (wrmode == 2) {
			strcat(cmd2," -tao defpregap=0");
		}
		if (wrmode == 3) {
			strcat(cmd2," -raw96r");
		}
		if (wrmode == 4) {
			strcat(cmd2," -raw96p");
		}
		if (wrmode == 5) {
			strcat(cmd2," -raw16");
		}
	} else {
		strcat(cmd2," -dao");
	}
	if (curset.writesimul) {
		strcat(cmd2," -dummy");
	}
	if (curset.writeeject) {
		strcat(cmd2," -eject");
	}
	if (curset.writepad) {
		strcat(cmd2," -pad");
	}
	if (curset.writeswap) {
		strcat(cmd2," -swab");
	}
	if (curset.writeoverburn) {
		strcat(cmd2," -overburn");
	}
	if (curset.writeignsize) {
		strcat(cmd2," -ignsize");
	}
	if (curset.writeimmed) {
		strcat(cmd2," -immed");
	}
	/*
	 * CD text can only be written in
	 * mode DAO or raw96r or raw96p
	 */
	if (curset.writecdtext && (wrmode == 0 || wrmode == 3 || wrmode == 4)) {
		strcat(cmd2," -text");
	}

	if (!isaudiocd) {
		/* now add track-specification: one data track, known length */
 	       if (sectsize == SUNDATASECTORSIZE) {
       	         	g_snprintf(tmp, MAXLINE, " tsize=%db", endsector);
        	} else {
                	g_snprintf(tmp, MAXLINE, " tsize=%ds", endsector);
        	}

		strcat(cmd2, tmp);
		strcat(cmd2," -waiti -data -");

		/* fill up percent values for nice progressbar movement */
		tracknr = 1;
		writeparams.tracktype[tracknr] = 0; /* data */
		writeparams.frames[tracknr] = endsector;
		writeparams.pct_this_track_arr[tracknr] = 1.0;	
		writeparams.pct_so_far_arr[tracknr] = 0.0;
		writeparams.nrtracks = 1;

	} else {

		strcat(cmd2," -waiti -audio");

		/* audio mode - add info-file list */
	        loop = g_list_first(trackreadset.trackparams);
        	while(loop) {
                	trackparam = loop->data;
                	if (trackparam->trackfile != NULL && 
                    	    trackparam->tracktype == 1) {
                        	strcpy(tmptmp, trackparam->trackfile);
                        	g_snprintf(tmp, MAXLINE, " \"%s\"",
                                	convert_escape(tmptmp));
                        	strcat(cmd2, tmp);
			}
			/* fill up percent values for nice progressbar movement */
			tracknr = trackparam->starttrack;
			writeparams.tracktype[tracknr] = trackparam->tracktype;
			writeparams.frames[tracknr] = trackparam->frames;
			writeparams.pct_this_track_arr[tracknr] = trackparam->percent;
			writeparams.pct_so_far_arr[tracknr] = pct_so_far;
			pct_so_far += trackparam->percent;
			writeparams.nrtracks++;

			loop = loop->next;
		}

	}

	/* save partial command line to view in progress window */
	g_snprintf(outcmd, MAXLINE,"Calling: %s\n\n", cmd2);

	dodebug(1, "spawning: %s | %s \n",cmd, cmd2);
	dolog(1,"Write Disc on the fly\n");
	dolog(3,"Executing: %s | %s\n", cmd, cmd2);

	/* output command to get better debug output */
	append_to_text_view(readtrack_textview, outcmd);

	/* yes...command line is done */
	full_dpl_pipe4(&pid1,&pid2,NULL,&read_err,cmd,&read_out,&read_err2,cmd2,1);
	mkisofs_pid = -1;
	readcdda_pid = pid2;
	readcdda_pid2 = pid1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_write_out, GINT_TO_POINTER((gint)pid2));

	gio2 = g_io_channel_unix_new(read_err2);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback2 = g_io_add_watch(gio2, G_IO_IN | G_IO_HUP,
                read_write_stderr, NULL);

	gio3 = g_io_channel_unix_new(read_err);
	g_io_channel_set_flags(gio3, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio3, NULL, NULL); 
        readcdda_callback3 = g_io_add_watch(gio3, G_IO_IN | G_IO_HUP,
                verify_readcd_err, GINT_TO_POINTER(pid1));

	/* disallow delete button press */
	set_dialog_disallow_delete();

	cdrecord_stdin = read_err2;

	/* now wait until write is finished */
	while (read_done == 999) {
		wait_and_process_events();

		read_write_idle_loop(pid2);
	}
	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);
	g_io_channel_unref(gio3);

	close(read_err2);
	close(read_err);
	close(read_out);

	set_dialog_allow_delete();
	cancel_show_dialog();

	/* remove temporary inf files */
	if (isaudiocd) {
		remove_onthefly_inf_files();
	}

	/* no disc */
	if (read_output_ctrl == 2) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("No Disc or wrong Disc loaded."));
		return 1;
	}

	/* cannot send cue sheet */
	if (read_output_ctrl == 4) {
		show_dialog(ICO_WARN,_("Your CD-Writer did not accept the CUE sheet needed for\nDAO writing. This could mean that cdrecord does not support DAO\nwith your writer. Select TAO mode and try again."),T_OK, NULL, NULL, 0);
	}

	/* aborted write ? */
	if (read_output_ctrl == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Writing aborted..."));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Write-Error:"));
		return 1;
	}

	/* error while reading ?*/
	if (read_done != 0 || read_output_ctrl == 5) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Error writing tracks"));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Write-Error:"));
		return 1;
	} else {
		/* all ok */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Tracks successfully written"));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Successful:"));
		return 0;
	}
}


/*
 * call cdrecord to fixate a disc
 */
gint start_write_fixate_only(gint devnr) {
char tmp[MAXLINE];
char tmp2[MAXLINE];
char cmd[MAXLINE*10];  /* extra big buffer for very long cdrecord options */
gint read_out, read_err;
gint wrmode;
GIOChannel *gio, *gio2;

	/* if another write running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* mark our write-process as running */
	read_done = 999;
	read_output_ctrl = 0;
	pct_so_far = 0.0;
	cdrecord_reload = 0;
	cdrtimer = 0;
	
	/* set info label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Initializing Disc Writer..."));

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}
		
	/* build command line */
	get_wrap_path("CDRECORD", tmp2);
	g_snprintf(cmd, MAXLINE*10,
		"%s %s gracetime=%d -v -fix", tmp2, tmp, get_gracetime());

	if (get_cur_writer_drvmode() >= 0) {
		if (drivers[get_cur_writer_drvmode()] != NULL) {
			g_snprintf(tmp,MAXLINE," driver=%s", 
				drivers[get_cur_writer_drvmode()]->driver);
			strcat(cmd,tmp);
		}
	}
	
	if (get_cur_writer_speed() >= 0) {
		g_snprintf(tmp,MAXLINE," speed=%d", get_cur_writer_speed());	
		strcat(cmd, tmp);
	}

	/*
	 * allow setting of write mode only for CD
	 * as for DVD and BD solely DAO is possible
	 */
	if (curset.cdrtype < 1000) {
		wrmode = get_cur_writemode();
		if (wrmode == 0) {
			strcat(cmd," -dao");
		}
		if (wrmode == 1) {
			strcat(cmd," -tao");
		}
		if (wrmode == 2) {
			strcat(cmd," -tao defpregap=0");
		}
		if (wrmode == 3) {
			strcat(cmd," -raw96r");
		}
		if (wrmode == 4) {
			strcat(cmd," -raw96p");
		}
		if (wrmode == 5) {
			strcat(cmd," -raw16");
		}
	} else {
		strcat(cmd," -dao");
	}
	if (curset.writesimul) {
		strcat(cmd," -dummy");
	}
	if (curset.writeeject) {
		strcat(cmd," -eject");
	}
        if (curset.writeignsize) {
                strcat(cmd," -ignsize");
        }
        if (curset.writeimmed) {
                strcat(cmd," -immed");
        }

	dodebug(1, "spawning: %s\n",cmd);
	dolog(1,"Fixate Disc\n");
	dolog(3,"Executing: %s\n", cmd);

	/* yes...command line is done */
	readcdda_pid = full_dpl_pipe3(&read_out,&cdrecord_stdin,&read_err,cmd,1);
	readcdda_pid2 = -1;
	mkisofs_pid = -1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_write_out, NULL);

	gio2 = g_io_channel_unix_new(read_err);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback2 = g_io_add_watch(gio2, G_IO_IN | G_IO_HUP,
                read_write_stderr, NULL);

	/* disallow delete button press */
	set_dialog_disallow_delete();

	/* now wait until write is finished */
	while (read_done == 999) {
		wait_and_process_events();

		read_write_idle_loop((pid_t)0);
	}

	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);

	close(read_err);
	close(read_out);
	close(cdrecord_stdin);

	set_dialog_allow_delete();
	cancel_show_dialog();

	/* no disc */
	if (read_output_ctrl == 2) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("No Disc or wrong Disc loaded."));
		return 1;
	}
	/* cannot send cue sheet */
	if (read_output_ctrl == 4) {
		show_dialog(ICO_WARN,_("Your CD-Writer did not accept the CUE sheet needed for\nDAO writing. This could mean that cdrecord does not support DAO\nwith your writer. Select TAO mode and try again."),T_OK, NULL, NULL, 0);
	}

	/* aborted write ? */
	if (read_output_ctrl == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Writing aborted..."));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Write-Error:"));
		return 1;
	}

	/* error while reading ?*/
	if (read_done != 0 || read_output_ctrl == 5) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Error writing tracks"));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Write-Error:"));
		return 1;
	} else {
		/* all ok */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Tracks successfully written"));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Successful:"));
		return 0;
	}
}


/*
 * parse output of rmtool and update sliders and textbox
 */
static gboolean read_delete_out_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gint val;
gfloat pval;

	/* read output of rmtool */
	read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

	dodebug(10,"rmtool: %s\n", line);

	/* look for a percent value */
	if (line[strlen(line)-1] == '%') {
		strcpy(tmp,line);
		tmp[strlen(tmp)-1] = '\0';
		strip_string(tmp);
		val = atoi(tmp);
		read_output_ctrl = val;
		pval = (gfloat)val/100;
		pval = (pval*delete_count + delete_start) /delete_all;
		if (pval > 1.0) pval = 1.0;
		gtk_progress_set_percentage(
			GTK_PROGRESS(readtrack_pbar1), pval);
		set_xcdr_title(toplevel,viewmode_dialog,(gint)(pval*100+0.5));
		return (TRUE);
	}	
			
	strcat(line,"\n");
	append_to_text_view(readtrack_textview, line);
 	
	return (TRUE);
}

static gboolean read_delete_out(GIOChannel *source, GIOCondition cond, gpointer data) {
gint ret;


	if (cond & G_IO_IN) {
        	read_delete_out_io_in(source, cond, data);
    	}

	/* delete finished */
	if (cond == G_IO_HUP) {

		/* pick up return status of child */
		ret = get_wait_ret(-1);
	 	dodebug(10,"Finished in read_delete_out with status: %d\n", ret);	

		/* tell our caller that we are done here */
		if (ret == 0 && read_output_ctrl != 100) {
			/* aborted */
			read_done = 2;
		} else {
			read_done = ret;
		}
		return (FALSE);
	}
    return (TRUE);
}


/*
 * does do the real deletion of some files
 */
static gint do_delete_files(gchar *cmd, gint count, gint start, gint all) {
gint read_out;
GIOChannel *gio;

	/* set some global vars we need for progress-bar */
	delete_count = count;
	delete_start = start;
	delete_all = all;

	dodebug(1,"spawning: %s\n",cmd);
	dolog(2,"Deleting %d files\n", count);
	dolog(3,"Executing: %s\n", cmd);

	readcdda_pid = full_dpl_pipe3(&read_out,NULL,NULL,cmd,0);
	readcdda_pid2 = -1;
	
	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_delete_out, NULL);

	/* now wait until tracks are deleted */
	while (read_done == 999) {
		wait_and_process_events();
	}

	g_io_channel_unref(gio);
	close(read_out);

	return read_done;
}


/*
 * do delete some tracks from the HD
 * but only attach 50 files of the bulk at one time
 * to ensure there is no overrun of shell arguments
 */
gint start_delete_action(GList *delfiles) {
GList *loop;
gchar *fname;
gint length, broke;
gint count,lastcount;
gchar tmp[MAXLINE];
gchar tmptmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar infname[MAXLINE];
gchar infname2[MAXLINE];
char orgcmd[MAXLINE];
char cmd[MAXLINE*10];
gchar *p;

	/* if another delete running, ignore */
	if (read_done == 999) {
		return -1;
	}

	read_done = 999;
	read_output_ctrl = 0;
	lastcount = 0;
	broke = 0;

	gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Deleting tracks..."));

	/* build of command line */
	g_snprintf(orgcmd, MAXLINE,"%s/%s ", sharedir, RMTOOL);
	strcpy(cmd, orgcmd);

	length = g_list_length(delfiles);
	count = 0;

	loop = g_list_first(delfiles);
	while(loop) {
		fname = (gchar *)loop->data;

		/* remove extension and put .inf there */
		strncpy(tmp,fname,MAXLINE - 4);
		p = rindex(tmp,'.');
		if (p == NULL) return -1;
		strcpy(tmp2,p);
		*p = '\0';
		if (strcmp(tmp2,".toc") == 0) {
			strcpy(tmptmp,fname);
			g_snprintf(tmp2, MAXLINE, "\"%s\" ",
				convert_escape(tmptmp));
		} else {
			strcpy(infname,tmp);
			strcat(infname,XCDROAST_INFO_EXT);
			strcpy(infname2,tmp);
			strcat(infname2,CDDA2WAV_INFO_EXT);
			strcpy(tmptmp,fname);
			g_snprintf(tmp2, MAXLINE, "\"%s\" \"%s\" \"%s\" ",
				convert_escape(tmptmp),
				convert_escape(infname), 
				convert_escape(infname2));
		}

		if (strlen(cmd) + strlen(tmp2) < MAXLINE*1024)
			strcat(cmd, tmp2);

		count++;
		if (count%DELETE_BLOCKS == 0) {
			/* part of commandline complete */
			read_done = do_delete_files(cmd, count-lastcount, lastcount, length);
			lastcount = count;
			strcpy(cmd, orgcmd);

			/* this run was successful? */
			if (read_done == 0) {
				/* but more files to delete coming */
				if (length != count)
					read_done = 999;
				read_output_ctrl = 0;
			} else {
				broke = 1;
				break;
			}
		}
		loop = loop->next;
	}

	/* any parts left over? (and no error before) */
	if (count%DELETE_BLOCKS != 0 && broke == 0) {
			/* part of commandline complete */
			read_done = do_delete_files(cmd, count-lastcount, lastcount, length);
	}

	/* all ok? */
	if (read_done == 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Delete successful"));
	} else 
	if (read_done == 2) {
		/* delete canceled */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Delete aborted..."));
	} else {
		/* some error */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Error deleting tracks"));
	}

	return 0;
}


/*
 * parse output of verify-tool and update sliders
 */
static gboolean verify_readcd_out_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gint val;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gfloat pval, pval2;

	/* read output of vrfytool  */
	read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

	dodebug(2,"verify-readcd (stdout): %s\n", line);

	/* look for a percent value */
	if (line[strlen(line)-1] == '%') {

		strcpy(tmp,line);
		tmp[strlen(tmp)-1] = '\0';
		strip_string(tmp);
		val = atoi(tmp);

		pval = (gfloat)val/100;
		if (pval > 1.0) pval = 1.0;

		gtk_progress_set_percentage(GTK_PROGRESS(readtrack_pbar1),
			pval);

		/* now calculate how much percent he has at all */
		pval2 = (pct_so_far + (pval * pct_this_track));
		if (pval2 > 1.0) pval2 = 1.0;
		gtk_progress_set_percentage(GTK_PROGRESS(readtrack_pbar2),
			pval2);
		set_xcdr_title(toplevel,viewmode_dialog,(gint)(pval2*100+0.5));

		/* now update info for small view */
		g_snprintf(tmp,MAXLINE,"%d%% / %d%%",(gint)(pval*100+0.5),
			   (gint)(pval2*100+0.5));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info2),tmp);	
		
		return (TRUE);
	}

	/* forward all other output to textview-window */
	strcat(line,"\n");
	append_to_text_view(readtrack_textview, line);

	return (TRUE);
}

static gboolean verify_readcd_out(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	if (cond & G_IO_IN) {
            verify_readcd_out_io_in(source, cond, data);
    	}

	if ((cond & G_IO_IN) && (cond & G_IO_HUP)) {
                read_io_channel_all(source, line, MAXLINE);
		if (strcmp(line, "") == 0) {
			cond = G_IO_HUP;
		} else {
			append_multiline_to_textview(line, "", readtrack_textview, "verify-readcd-hup (stdout)");
		}
    	}

	/* readcd-finished? */
	if (cond == G_IO_HUP) {

		/* pick up return status of child */
		read_done = get_wait_ret(GPOINTER_TO_INT(data));
		return (FALSE);
	}

    return (TRUE);
}

/*
 * do just swallow all output we get from stderr...
 * and wait if the process terminated
 */
static gboolean verify_readcd_err(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	if (cond & G_IO_IN) {

	    /* read output of vrfytool */
	    read_io_channel_all(source, line, MAXLINE);

	    /* nothing read - nonblock */
	    if (strcmp(line, "") == 0) {
		    wait_and_process_events();
	    } else {
	        dodebug(10,"readcd/cdda2wav (stderr): %s\n", line);
            }
    	}

	if ((cond & G_IO_IN) && (cond & G_IO_HUP)) {
                read_io_channel_all(source, line, MAXLINE);
		if (strcmp(line, "") == 0) {
			cond = G_IO_HUP;
		} else {
			append_multiline_to_textview(line, "", NULL, "readcd/cdda2wav-hup (stderr)");
		}
    	}

	/* readcd-finished? */
	if (cond == G_IO_HUP) {

                /* pick up return status of child */
		get_wait_ret(GPOINTER_TO_INT(data));
                dodebug(10,"Finished in verify_readcd_err\n");
		return (FALSE);
	}

	return (TRUE);
}


/*
 * does call readcd | vrfytool to do verifying
 */
gint verify_data_track(gint devnr, gint starttrack, gint kbyte, 
		      gchar *fname, gint startoffset, gint endoffset,
		      gint nrtracks, gfloat percent, gfloat percent_done,
		      gint viewtrack) {
gchar cmd[MAXLINE];
gchar cmd2[MAXLINE];
gchar tmp[MAXLINE];
gchar tmptmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gint read_out, read_err, sectsize;
pid_t pid1, pid2;
GIOChannel *gio, *gio2;

	/* if another read running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* no filename given? */
	if (fname == NULL) {
		return 1;
	}

	/* mark our read-process as running */
	read_done = 999;
	readcdda_pid = -1;
	readcdda_pid2 = -1;
	read_output_ctrl = 0;
	read_abort_mark = 0;

	/* set info-label */
	convert_kbytes2mbminstring(kbyte, tmp);
	g_snprintf(tmp2,MAXLINE,_("Verifying data track %d/%d [%s]"), viewtrack, nrtracks, tmp);
	gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp2);	

	g_snprintf(tmp2,MAXLINE,_("Verifying %d/%d:"), viewtrack, nrtracks);
	gtk_label_set_text(GTK_LABEL(readtrack_small_info),tmp2);	
	
	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* some stuff to have our slider moving smoothly no matter how
	   big the tracks are */
	pct_so_far = percent_done;
	pct_this_track = percent;

        /* now check which sectorsize we got */
        sectsize = get_sectorsize(devnr);
	if (sectsize == 2352) {
		/* workaround for buggy drives */
        	endoffset = ((endoffset * DATASECTORSIZE) / sectsize) + 1;
	}

	get_wrap_path("READCD", tmp3);
	g_snprintf(cmd,MAXLINE,
		   "%s %s sectors=%d-%d -s f=-",
		   tmp3, tmp, startoffset, endoffset);
	
	strcpy(tmptmp, fname);

	/* always verify in 512 bytes blocks */
	g_snprintf(cmd2,MAXLINE,"%s/%s -b \"%s\" -", 
		   sharedir, VRFYTOOL, convert_escape(tmptmp));

	dodebug(1, "spawning: %s | %s\n",cmd,cmd2);
	dolog(2, "Verify data track %s\n", fname);
	dolog(3, "Executing: %s | %s\n", cmd, cmd2);

	/* start childs and get new fds */
	full_dpl_pipe4(&pid1,&pid2,NULL,&read_err,cmd,&read_out,NULL,cmd2,0);
	readcdda_pid = pid1;
	readcdda_pid2 = -1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                verify_readcd_out, GINT_TO_POINTER(pid2));

	gio2 = g_io_channel_unix_new(read_err);
	g_io_channel_set_flags(gio2, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback3 = g_io_add_watch(gio2, G_IO_IN | G_IO_HUP,
                verify_readcd_err, GINT_TO_POINTER(pid1));

	/* now wait until track is read */
	while (read_done == 999) {
		wait_and_process_events();
	}

	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);

	close(read_out);
	close(read_err);

	append_to_text_view(readtrack_textview, "\n");

	/* error while reading? */
	if (read_done != 0 && read_abort_mark == 0) {
		g_snprintf(tmp,MAXLINE,_("Verifying data track %d/%d failed"), starttrack, nrtracks);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);	
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Verify-Error:"));	
		return 1;
	}
	/* compare aborted? */
	if (read_abort_mark != 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Verify aborted..."));	
		return 2;
	}

	return 0;
}


/*
 * does call cdda2wav | vrfytool to do verifying
 */
gint verify_audio_track(gint devnr, gint starttrack, gint endtrack, gint kbyte, 
		      gchar *fname, gint startoffset, gint endoffset,
		      gint nrtracks, gfloat percent, gfloat percent_done,
		      gint viewtrack) {
gchar cmd[MAXLINE];
gchar cmd2[MAXLINE];
gchar tmp[MAXLINE];
gchar tmptmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gchar tmp4[MAXLINE];
gchar tmp6[MAXLINE];
gchar tmp8[MAXLINE];
gchar tmp9[MAXLINE];
gint read_out, read_err;
pid_t pid1, pid2;
GIOChannel *gio, *gio2;

	/* if another read running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* no filename given? */
	if (fname == NULL) {
		return 1;
	}

	/* mark our read-process as running */
	read_done = 999;
	readcdda_pid = -1;
	readcdda_pid2 = -1;
	read_output_ctrl = 0;
	read_abort_mark = 0;

	/* set info-label */
	convert_kbytes2mbminstring(kbyte, tmp);
	g_snprintf(tmp2,MAXLINE,_("Verifying audio track %d/%d [%s]"), viewtrack, nrtracks, tmp);
	gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp2);	

	g_snprintf(tmp2,MAXLINE,_("Verifying %d/%d:"), viewtrack, nrtracks);
	gtk_label_set_text(GTK_LABEL(readtrack_small_info),tmp2);	
	
	/* get bus,id,lun string */
	if (convert_devnr2busid(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* some stuff to have our slider moving smoothly no matter how
	   big the tracks are */
	pct_so_far = percent_done;
	pct_this_track = percent;

	/* build command line */
	if (endtrack != 0) {
		g_snprintf(tmp2,MAXLINE,"%d+%d",starttrack,endtrack);
	} else {
		g_snprintf(tmp2,MAXLINE,"%d",starttrack);
	}

	/* set speed only when not zero */
	if (get_cur_audioread_speed() > 0) {
		g_snprintf(tmp3,MAXLINE,"-S %d", get_cur_audioread_speed()); 
	} else {
		strcpy(tmp3,"");
	}

	/* set paranoia options */
	if (get_cur_audioread_useparanoia() && (get_cur_audioread_paranoiaminoverlap() < 1)) {
		g_snprintf(tmp6, MAXLINE, "paraopts=retries=%d,readahead=%d",
			   get_cur_audioread_paranoiaretries(),get_cur_audioread_paranoiareadahead());
	} else if (get_cur_audioread_useparanoia()) {
		g_snprintf(tmp6, MAXLINE, "paraopts=retries=%d,readahead=%d,minoverlap=%d",
			   get_cur_audioread_paranoiaretries(),get_cur_audioread_paranoiareadahead(),get_cur_audioread_paranoiaminoverlap());
	} else {
		g_snprintf(tmp6, MAXLINE, "-P %d", get_cur_audioread_overlap());
	}

	/* have to deemphasize? */
	if (curset.deemphasize) {
		strcpy(tmp9,"-deemphasize");
	} else {
		strcpy(tmp9,"");
	}

	/* have to enable -no-hidden-track? */
	if (get_cur_audioread_showhiddentrack()) {
		strcpy(tmp8,"");
	} else {
		strcpy(tmp8,"-no-hidden-track");
	}

	get_wrap_path("CDDA2WAV", tmp4);
	g_snprintf(cmd,MAXLINE,
		   "%s -D \"%s\" -g -no-infofile -v toc -O wav -t %s %s %s %s %s -",
		   tmp4,tmp,tmp2,tmp3,tmp6,tmp8,tmp9);
	
	strcpy(tmptmp,fname);
	g_snprintf(cmd2,MAXLINE,"%s/%s -a \"%s\" -", 
		   sharedir, VRFYTOOL, convert_escape(tmptmp));

	dodebug(1, "spawning: %s | %s\n",cmd,cmd2);
	dolog(2, "Verify audio track %s\n", fname);
	dolog(3, "Executing: %s | %s\n", cmd, cmd2);

	/* start childs and get new fds */
	full_dpl_pipe4(&pid1,&pid2,NULL,&read_err,cmd,&read_out,NULL,cmd2,0);
	readcdda_pid = pid1;
	readcdda_pid2 = -1;

	/* catch output of child */
	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                verify_readcd_out, GINT_TO_POINTER(pid2));

	gio2 = g_io_channel_unix_new(read_err);
	g_io_channel_set_flags(gio2, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback3 = g_io_add_watch(gio2, G_IO_IN | G_IO_HUP,
                verify_readcd_err, GINT_TO_POINTER(pid1));

	/* now wait until track is read */
	while (read_done == 999) {
		wait_and_process_events();
	}

	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);

	close(read_out);
	close(read_err);

	append_to_text_view(readtrack_textview, "\n");

	/* error while reading? */
	if (read_done != 0 && read_abort_mark == 0) {
		g_snprintf(tmp,MAXLINE,_("Verifying audio track %d/%d failed"), starttrack, nrtracks);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);	
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Verify-Error:"));	
		return 1;
	}
	/* compare aborted? */
	if (read_abort_mark != 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Verify aborted..."));	
		return 2;
	}

	return 0;
}


/*
 * parse output of cddbtool and update textbox
 */
static gboolean read_cddbtool_out_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gint code, mode;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar *p;
gchar *rowdata[1];

	/*
	 * mode 0 for CD
	 * mode 1 for tocfile
	 * mode 2 for on-the-fly
	 */
	mode = GPOINTER_TO_INT(data);

	/* read output of readcd */
	read_io_channel(source, line, MAXLINE);
	dodebug(10, "cddbtool: %s\n", line);

	/* got we the number of matches? */
	if (line[0] == '%') {
		strcpy(tmp,line+1);
		matchnr = atoi(tmp);
		return (TRUE);
	}

	/* got we a entry for the match list? */
	if (line[0] == '#') {
		/* end-delimitor found */
		if (strncmp(line,"#--",3) == 0) {
			/* now see if we have only one match - preselect */
			if (matchnr == 1) {
				gtk_clist_select_row(cddb_clist,0,0);
			}
			return (TRUE);
		}
		/* add this to the clist */
		strcpy(tmp,line);
		if (extract_quoted(tmp) != 0) 
			return (TRUE);

		/* now add decoded string to clist */
		rowdata[0] = convert_for_gtk2(tmp);
		gtk_clist_append(cddb_clist,rowdata);
		return (TRUE);
	}

	/* get output id of read line */
	strcpy(tmp,line);
	p = strtok(tmp,":");
	if (p == NULL) {
		return (TRUE);
	}
	code = atoi(p);

	strcpy(tmp,"");

	/* display fitting text */
	switch(code) {
	case 0: 
		g_snprintf(tmp,MAXLINE,_("Connecting to %s:%d"), 
			setupdata.cddb_host, setupdata.cddb_port);
		break;
	case 1: 
		strcpy(tmp,_("Connect ok: Sending handshake"));
		break;
	case 2: 
		strcpy(tmp,_("Sending CDDB-query"));
		break;
	case 3: 
		strcpy(tmp,_("No matches found - unknown CD"));
		break;
	case 4: 
		/* close matches */
		if (matchnr == 1) {
			strcpy(tmp,_("Found one close match"));
		} else {
			g_snprintf(tmp,MAXLINE,_("Found %d close matches - Please select"), matchnr);
		}
		break;
	case 5: 
		/* exact matches */
		if (matchnr == 1) {
			strcpy(tmp,_("Found one exact match"));
		} else {
			g_snprintf(tmp,MAXLINE,_("Found %d exact matches - Please select"), matchnr);
		}
		break;
	case 6: 
		strcpy(tmp,_("Requesting data - Please wait"));
		break;
	case 7:
		/* got disc-title */
		strcpy(tmp,line);
		if (extract_quoted(tmp) != 0) 
			return (TRUE);

		/* we want title first, then artist */
		if (mode == 0) {
			switch_artist_title(tmp);	
			g_free(cdinfo.cddb_dtitle);
			cdinfo.cddb_dtitle = g_strdup(tmp);

			dolog(3, "CDDB lookup done for %s\n", cdinfo.cddb_dtitle);
		} else {
			cdtext_set_dtitle(tmp);
			dolog(3, "CDDB lookup done for %s\n", tmp);
		}

		/* recycle matchnr-var as track-counter */
		matchnr = 0;

		return (TRUE);
	case 8:
		/* got track-title */
		strcpy(tmp,line);
		if (extract_quoted(tmp) != 0) 
			return (TRUE);

		if (matchnr == 0 && trackinfo[matchnr]->track_nr == 0)
			matchnr++;

		if (mode == 0) {
			if (matchnr >= cdinfo.nr_tracks) {
				g_warning("invalid track count from cddb-server\n");
				return (TRUE);
			}

			g_free(trackinfo[matchnr]->cddb_ttitle);
			trackinfo[matchnr]->cddb_ttitle = g_strdup(tmp);
		} else {
			cdtext_set_ttitle(tmp, matchnr+1);
		}
		matchnr++;
		return (TRUE);
		
	/* Error-codes */
	case -1:
		strcpy(tmp,_("Error: Hostname lookup failure"));
		break;
	case -2:
		strcpy(tmp,_("Error: Can't open stream socket"));
		break;
	case -3:
		strcpy(tmp,_("Error: Connection refused"));
		break;
	case -4:
		strcpy(tmp,_("Error: No response from server"));
		break;
	case -5:
		strcpy(tmp,_("Error: No answer within timeout"));
		break;
	case -6:
		strcpy(tmp,_("Handshake failed - No valid CDDB-server?"));
		break;
	case -7:
		strcpy(tmp,_("Error: CDDB-query failed"));
		break;
	case -8:
		strcpy(tmp,_("Error: CDDB-read failed"));
		break;
	case -9:
		strcpy(tmp,_("Warning: CDDB-logout failed"));
		break;
	case -10:
		strcpy(tmp,_("Error: CDDB write error on socket"));
		break;
	case -11:
		strcpy(tmp,_("Error: CDDB read error on socket"));
		break;
	}

	gtk_label_set_text(GTK_LABEL(cddb_info_label), tmp);

	return (TRUE);
}

static gboolean read_cddbtool_out(GIOChannel *source, GIOCondition cond, gpointer data) {

	if (cond & G_IO_IN) {
            read_cddbtool_out_io_in(source, cond, data);
    	}

	/* cddbtool finished */
	if (cond == G_IO_HUP) {

		/* pick up return status of child */
		get_wait_ret(-1);

		if (matchnr > 0)
			/* got track-names */
			read_done = 1;
		else 
			read_done = 0;
		return (FALSE);
	}
    return (TRUE);
}

/*
 * do lookup the cddb-database
 * mode = 0, cddb lookup based on CD
 * mode = 1, cddb lookup based on toc-file 
 * mode = 2, cddb lookup based on CD but called from edit-cdtext
 * return 1 if matches were found and read, 0 on no matches 
 */
gint start_cddb_lookup_action(gint mode) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp_cddbhost[MAXLINE];
gchar tmp_cddbproxy[MAXLINE];
char cmd[MAXLINE];
track_read_param_t *trackparam;
GList *loop;
GIOChannel *gio;
gint read_out;
gint i;

	/* if another lookup running, ignore */
	if (read_done == 999) {
		return -1;
	}

	read_done = 999;
	read_output_ctrl = 0;
	matchnr = 0;

	/* build of command line */
	if (mode == 0 || mode == 2) {
		gint	ntr = cdinfo.nr_tracks;

		if (ntr > 0 && trackinfo[0]->track_nr == 0)
			ntr--;
		g_snprintf(tmp,MAXLINE,"cddb query %s %d ",
			cdinfo.cddb_discid, ntr);
		for (i = 0; i < cdinfo.nr_tracks; i++) {
			if (trackinfo[i]->track_nr == 0)
				continue;
			g_snprintf(tmp2,MAXLINE,"%d ",
				trackinfo[i]->start_sec + 150);
			strcat(tmp,tmp2);
		}
		g_snprintf(tmp2,MAXLINE,"%d",(cdinfo.leadout + 150) / 75);
		strcat(tmp,tmp2);
	} else {
		/* get data from toc file */
		g_snprintf(tmp,MAXLINE,"cddb query %s %d ",
			trackreadset.cd_discid, trackreadset.nrtracks);
		loop = g_list_first(trackreadset.trackparams);
		for (i = 0; i < trackreadset.nrtracks; i++) {
			trackparam = loop->data;
			if (trackparam) {
				g_snprintf(tmp2,MAXLINE,"%d ",
					trackparam->start_sec + 150);
				strcat(tmp,tmp2);
			} else {
				g_warning("trackreadset corruption detected.\n");
			}
			loop = loop->next;
		}
		g_snprintf(tmp2,MAXLINE,"%d",trackreadset.cdsize / 75);
		strcat(tmp,tmp2);
	}

	strncpy(tmp_cddbhost, setupdata.cddb_host, MAXLINE);
	convert_escape(tmp_cddbhost);
	strncpy(tmp_cddbproxy, setupdata.cddb_proxy_host, MAXLINE);
	convert_escape(tmp_cddbproxy);

	if (setupdata.cddb_use_http == 0) {
	  	g_snprintf(cmd,MAXLINE,
		"%s/%s -s \"%s\" -p %d -u \"%s\" -h \"%s\" -q \"%s\"", 
		sharedir, CDDBTOOL, tmp_cddbhost, setupdata.cddb_port,
		username, hostname, tmp);
	} else {
		/* we want to use http */
		if (setupdata.cddb_use_proxy == 0) {
			/* but no proxy */
	  		g_snprintf(cmd,MAXLINE,
			"%s/%s -s \"%s\" -u \"%s\" -h \"%s\" -H -q \"%s\"", 
			sharedir, CDDBTOOL, tmp_cddbhost, 
			username, hostname, tmp);
		} else {
			/* http with proxy */
	  		g_snprintf(cmd,MAXLINE,
			"%s/%s -s \"%s\" -u \"%s\" -h \"%s\" -U \"%s\" -P %d -q \"%s\"", 
			sharedir, CDDBTOOL, tmp_cddbhost, 
			username, hostname, tmp_cddbproxy,
			setupdata.cddb_proxy_port, tmp);
		}
	}	
	dodebug(1, "spawning: %s\n",cmd);
	if (mode == 0 || mode == 2) {
		dolog(2, "CDDB lookup for discid %s\n", cdinfo.cddb_discid);
	} else {
		dolog(2, "CDDB lookup for discid %s (from toc-file)\n", trackreadset.cd_discid);
	}
	dolog(3, "Executing: %s\n", cmd);
	
	readcdda_pid = full_dpl_pipe3(&read_out,&cddb_in,NULL,cmd,0);
	readcdda_pid2 = -1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_encoding(gio, NULL, NULL); 
	readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
		read_cddbtool_out, GINT_TO_POINTER(mode));


	/* now wait until track is read */
	while (read_done == 999) {
		wait_and_process_events();
	}

	g_io_channel_unref(gio);

	close(read_out);
	close(cddb_in);

	return read_done;
}


/*
 * the user selected a specific row on cddb-window
 */
gint continue_cddb_lookup_action(gint match) {
gchar tmp[MAXLINE];

	dodebug(2, "sending %d to cddbtool\n", match);

	/* send this match back to our cddbtool-process */
	g_snprintf(tmp,MAXLINE,"%d\n",match);
	if (write(cddb_in, tmp, strlen(tmp)) != strlen(tmp)) {
		g_warning("write error to cddbtool pipe\n");
		return 1;
	}
	return 0;
}


/*
 * call wavplay test.wav
 */
void test_dspdevice_play() {
gchar line[MAXLINE];
FILE *fpin;

	/* allocate memory */
	g_snprintf(line,MAXLINE,"%s/%s -d \"%s\" \"%s/%s\" 2>&1", sharedir, 
			WAVPLAY, setupdata.dsp_device, sharedir, DSPTESTSOUND);

	dodebug(1, "calling: %s\n", line);

        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10, "dsptest: %s", line);
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

}


/*
 * Save the current iso-options to a file - all strings are converted
 * in a printable form first:  return 0 if ok, or 1 on error
 */
gint save_isooptions_file(gchar *confdir, gchar *fname) {
FILE *fd;
gchar tmp[MAXLINE];
gint i;

        /* now check if the confdir exists */
        if (!is_directory(confdir)) {
                /* try to create directory */
                mkdir(confdir, 0700);
                dodebug(2, "trying to mkdir %s\n", confdir);
        }

	g_snprintf(tmp,MAXLINE,"%s/%s", confdir, fname);

        dodebug(1, "Opening %s for writing\n", tmp);
        dolog(3, "Saving isooptions file %s\n", tmp);

        fd = fopen(tmp,"w"); 

        if (fd == NULL) { 
                /* error opening file */
                return 1;
        }

        /* write the config-file header */
        fputs("#\n",fd);
        g_snprintf(tmp,MAXLINE,"# X-CD-Roast %s Iso-Options-File\n",XCDROAST_VERSION);
        fputs(tmp,fd);
        fputs("#\n",fd);
        fputs("# Automatically created by the X-CD-Roast-Setup\n",fd);
        fputs("# Don't edit! (Unless you REALLY know what you are doing)\n",fd);
        fputs("#\n\n",fd);

        /* write data */
        g_snprintf(tmp,MAXLINE,"VERSION = \"%s\"\n",XCDROAST_VERSION); 
        fputs(tmp,fd);
        g_snprintf(tmp,MAXLINE,"PLATFORM = \"%s\"\n",system_platform); 
        fputs(tmp,fd);

	for (i=0; i<24; i++) {
		g_snprintf(tmp,MAXLINE,"OPT_%d = %d\n", i, 
			masterparam.opt[i]);
        	fputs(tmp,fd);
	}

        g_snprintf(tmp,MAXLINE,"IN_CHARSET = \"%s\"\n",
		charset_types[masterparam.charset]);
        fputs(tmp,fd);
        g_snprintf(tmp,MAXLINE,"CUST_IN_CHARSET = \"%s\"\n",
		masterparam.cust_charset);
        fputs(tmp,fd);
        g_snprintf(tmp,MAXLINE,"OUT_CHARSET = \"%s\"\n",
		charset_types[masterparam.outcharset]);
        fputs(tmp,fd);
        g_snprintf(tmp,MAXLINE,"CUST_OUT_CHARSET = \"%s\"\n",
		masterparam.cust_outcharset);
        fputs(tmp,fd);

	g_snprintf(tmp,MAXLINE,"SHOW_ONLY_DIRS = %d\n", 
			masterparam.show_only_dirs);
       	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"SHOW_HIDDEN_FILES = %d\n", 
			masterparam.show_hidden_files);
       	fputs(tmp,fd);

        if (fclose(fd) != 0) {
                /* error closing file */
                return 1;
        }

        return 0;
}


/*
 * Load the iso-options-default file  
 * return 0 if ok, or 1 on error
 */
gint load_isooptions_file(gchar *fname) {
FILE *fd;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];
gchar tmp[MAXLINE];
gint optval, optnr, i;

        if ((fd = fopen(fname,"r")) == NULL) { 
                /* error opening file */
        	dodebug(1, "Failed to open iso options file %s\n", fname);
        	dolog(3, "Failed loading iso options file %s\n", fname);
                return 1;
        }

        dodebug(1, "Opening iso options file %s for reading\n", fname);
        dolog(3, "Loading iso options file %s\n", fname);

        for (;;) {
                if (fgets(line,MAXLINE,fd) == NULL)
                        break;

                dodebug(10,"isooptions: %s", line),

                /* skip empty or hashed lines */
                strip_string(line);
                if (*line == '#' || *line == '\0') 
                        continue;

                /* parse lines */
		if (parse_config_line(line,id,value)) {
                	g_error("syntax error in isooptions-file\n");
		}	

		if (strncmp("OPT_",id,4) == 0) {
			strcpy(tmp,id+4);
			optnr = atoi(tmp);
			optval = atoi(value);
			if (optnr < 24) 
				masterparam.opt[optnr] = optval;
		}

                if (strncmp("IN_CHARSET",id,7) == 0) {
                        masterparam.charset = 0;
                        i = 0;
                        while (charset_types[i] != NULL) {
                                if (strcmp(charset_types[i],value) == 0) {
                                        masterparam.charset = i;
                                        break;
                                }
                                i++;
                        }
                }
                if (strncmp("CUST_IN_CHARSET",id,12) == 0) {
                        strcpy(masterparam.cust_charset,value);
                }
                if (strncmp("OUT_CHARSET",id,7) == 0) {
                        masterparam.outcharset = 0;
                        i = 0;
                        while (charset_types[i] != NULL) {
                                if (strcmp(charset_types[i],value) == 0) {
                                        masterparam.outcharset = i;
                                        break;
                                }
                                i++;
                        }
                }
                if (strncmp("CUST_OUT_CHARSET",id,12) == 0) {
                        strcpy(masterparam.cust_outcharset,value);
                }

		if (strcmp("SHOW_ONLY_DIRS",id) == 0) {
			masterparam.show_only_dirs = atoi(value);
		}
		if (strcmp("SHOW_HIDDEN_FILES",id) == 0) {
			masterparam.show_hidden_files = atoi(value);
		}

	}

        if (fclose(fd) != 0) {
                /* error closing file */
                return 1;
        }

        return 0;
}


/*
 * Save the current iso-headers to a file - all strings are converted
 * in a printable form first:  return 0 if ok, or 1 on error
 */
gint save_isoheaders_file(gchar *confdir, gchar *fname) {
FILE *fd;
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];

        /* now check if the confdir exists */
        if (!is_directory(confdir)) {
                /* try to create directory */
                mkdir(confdir, 0700);
                dodebug(2, "trying to mkdir %s\n", confdir);
        }

	g_snprintf(tmp,MAXLINE,"%s/%s", confdir, fname);

        dodebug(1, "Opening %s for writing\n", tmp);
        dolog(3, "Saving isoheaders file %s\n", tmp);

        fd = fopen(tmp,"w"); 

        if (fd == NULL) { 
                /* error opening file */
                return 1;
        }

        /* write the config-file header */
        fputs("#\n",fd);
        g_snprintf(tmp,MAXLINE,"# X-CD-Roast %s Iso-Headers-File\n",XCDROAST_VERSION);
        fputs(tmp,fd);
        fputs("#\n",fd);
        fputs("# Automatically created by the X-CD-Roast-Setup\n",fd);
        fputs("# Don't edit! (Unless you REALLY know what you are doing)\n",fd);
        fputs("#\n\n",fd);

        /* write data */
        g_snprintf(tmp,MAXLINE,"VERSION = \"%s\"\n",XCDROAST_VERSION); 
        fputs(tmp,fd);
        g_snprintf(tmp,MAXLINE,"PLATFORM = \"%s\"\n",system_platform); 
        fputs(tmp,fd);

	strcpy(tmp2,masterparam.publisher);
	g_snprintf(tmp,MAXLINE,"PUBLISHER = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	strcpy(tmp2,masterparam.preparer);
	g_snprintf(tmp,MAXLINE,"PREPARER = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	/* *** currently not used! ***
	strcpy(tmp2,masterparam.application);
	g_snprintf(tmp,MAXLINE,"APPLICATION = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	*/
	strcpy(tmp2,masterparam.abstract);
	g_snprintf(tmp,MAXLINE,"ABSTRACT = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	strcpy(tmp2,masterparam.biblio);
	g_snprintf(tmp,MAXLINE,"BIBLIO = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	strcpy(tmp2,masterparam.copyright);
	g_snprintf(tmp,MAXLINE,"COPYRIGHT = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);

        if (fclose(fd) != 0) {
                /* error closing file */
                return 1;
        }

        return 0;
}


/*
 * Load the iso-headers-default file  
 * return 0 if ok, or 1 on error
 */
gint load_isoheaders_file(gchar *fname) {
FILE *fd;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];

        if ((fd = fopen(fname,"r")) == NULL) { 
                /* error opening file */
        	dodebug(1, "Failed to open iso headers file %s\n", fname);
        	dolog(3, "Failed loading iso headers file %s\n", fname);
                return 1;
        }

        dodebug(1, "Opening iso headers file %s for reading\n", fname);
        dolog(3, "Loading iso headers file %s\n", fname);

        for (;;) {
                if (fgets(line,MAXLINE,fd) == NULL)
                        break;

                dodebug(10,"isoheaders: %s", line),

                /* skip empty or hashed lines */
                strip_string(line);
                if (*line == '#' || *line == '\0') 
                        continue;

                /* parse lines */
		if (parse_config_line(line,id,value)) {
                	g_error("syntax error in isoheaders-file\n");
		}	

		if (strcmp("PUBLISHER",id) == 0) {
			g_free(masterparam.publisher);
			masterparam.publisher = g_strdup(value);
		}
		if (strcmp("PREPARER",id) == 0) {
			g_free(masterparam.preparer);
			masterparam.preparer = g_strdup(value);
		}
		if (strcmp("APPLICATION",id) == 0) {
			g_free(masterparam.application);
			masterparam.application = g_strdup(value);
		}
		if (strcmp("ABSTRACT",id) == 0) {
			g_free(masterparam.abstract);
			masterparam.abstract = g_strdup(value);
		}
		if (strcmp("BIBLIO",id) == 0) {
			g_free(masterparam.biblio);
			masterparam.biblio = g_strdup(value);
		}
		if (strcmp("COPYRIGHT",id) == 0) {
			g_free(masterparam.copyright);
			masterparam.copyright = g_strdup(value);
		}
	}

        if (fclose(fd) != 0) {
                /* error closing file */
                return 1;
        }

        return 0;
}


/*
 * print master dirs and excludes (debug purposes)
 */
static void print_master_dirs() {
GList *loop;
mstr_redirect_t *mstr;
gchar *p;

	dodebug(2,"------ directories to master -----\n");
	loop = g_list_first(masterparam.mstr_redir);
	while (loop) {
		mstr = (mstr_redirect_t *) loop->data;

		if (mstr) {
			if (mstr->mstr_path && !mstr->redir_path) {
				dodebug(2,"master-dir: %s\n", mstr->mstr_path);
			}
			if (mstr->mstr_path && mstr->redir_path) {
				dodebug(2,"master-dir: %s -> %s\n", 
					mstr->mstr_path, mstr->redir_path);
			}
		}
		loop = loop->next;
	}

	dodebug(2,"------ directories to excluded from master -----\n");
	loop = g_list_first(masterparam.exclude_paths);
	while (loop) {
		p = (gchar *)loop->data;
		if (p) {
			dodebug(2,"master-exclude: %s\n", p);
		}
		loop = loop->next;
	}
}


/*
 * build mkisofs-commandline
 */
static void build_mkisofs_cmdline(gchar *ret, gint printsize, gchar *outfile, gint verbose) {
gchar line[MAXLINE*10];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE*2];
gchar tmp3[MAXLINE];
gchar tmp34[MAXLINE];
gchar tmp35[MAXLINE];
gchar tmp4[MAXLINE];
gchar tmp5[MAXLINE];
gchar tmp6[MAXLINE];
gchar tmp7[MAXLINE];
gchar tmptmp[MAXLINE];
gchar tmptmp2[MAXLINE];
gchar tmptmp3[MAXLINE];
GList *loop;
mstr_redirect_t *mstr;
gchar *p;
FILE *fd;
gint pathcount;

	/* check first if we got our temporary files? */
	if (!master_fname1 || !master_fname2) {
		g_warning("no temporary files for master? aborting...\n");

		/* return some command that will fail eventually gracefully */
		strcpy(ret, "false");
		return;
	}

	if (debug > 1) {
		print_master_dirs();
	}

	get_wrap_path("MKISOFS", tmp);

	/* extract iso-options */
	if (verbose) {
		strcpy(tmp2,"-gui -v ");
	} else {
		strcpy(tmp2," ");
	}

	if (masterparam.opt[0]) {
		strcat(tmp2,"-r ");
	}
	if (masterparam.opt[1]) {
                strcat(tmp2,"-J ");
	}
	if (masterparam.charset != 0) {
                strcat(tmp2,"-input-charset ");
		if (strcmp(charset_types[masterparam.charset],"utf-8") == 0) {
			strcat(tmp2,"iconv:UTF-8");
		} else if (strcmp(charset_types[masterparam.charset],"custom") == 0) {
			remove_illegal_chars_iconv(masterparam.cust_charset);
			if (masterparam.cust_charset != NULL && strlen(masterparam.cust_charset) > 0
			  && strncmp(masterparam.cust_charset,"custom",6) != 0) { /* just the placeholder? */
				strcat(tmp2,"iconv:");
				strcat(tmp2,masterparam.cust_charset);
			} else {
				strcat(tmp2,"default");
			}
		} else {
			strcat(tmp2,charset_types[masterparam.charset]);
		}
                strcat(tmp2," ");
	}
	if (masterparam.outcharset != 0) {
                strcat(tmp2,"-output-charset ");
		if (strcmp(charset_types[masterparam.outcharset],"utf-8") == 0) {
			strcat(tmp2,"iconv:UTF-8");
		} else if (strcmp(charset_types[masterparam.outcharset],"custom") == 0) {
			remove_illegal_chars_iconv(masterparam.cust_outcharset);
			if (masterparam.cust_outcharset != NULL && strlen(masterparam.cust_outcharset) > 0
			  && strncmp(masterparam.cust_outcharset,"custom",6) != 0) { /* just the placeholder? */
				strcat(tmp2,"iconv:");
				strcat(tmp2,masterparam.cust_outcharset);
			} else {
				strcat(tmp2,"default");
			}
		} else {
			strcat(tmp2,charset_types[masterparam.outcharset]);
		}
                strcat(tmp2," ");
	}
	if (masterparam.opt[2]) {
		strcat(tmp2,"-R ");
	}
	if (masterparam.opt[3]) {
		strcat(tmp2,"-f ");
	}
	if (masterparam.opt[4]) {
		strcat(tmp2,"-l ");
	}
	if (masterparam.opt[5]) {
		strcat(tmp2,"-T ");
	}
	if (masterparam.opt[6]) {
		strcat(tmp2,"-D ");
	}
	if (masterparam.opt[7]) {
		strcat(tmp2,"-allow-leading-dots ");
	}
	if (masterparam.opt[8]) {
		strcat(tmp2,"-d ");
	}
	if (masterparam.opt[9]) {
		strcat(tmp2,"-N ");
	}
	if (masterparam.opt[10]) {
		strcat(tmp2,"-U ");
	}
	if (masterparam.opt[11]) {
		strcat(tmp2,"-no-bak ");
	}
	if (masterparam.opt[12]) {
		strcat(tmp2,"-hide-rr-moved ");
	}
	if (masterparam.opt[13]) {
		strcat(tmp2,"-hide-joliet-trans-tbl ");
	}
	if (masterparam.opt[14]) {
		strcat(tmp2,"-no-rr ");
	}
        if (masterparam.opt[15]) {
                strcat(tmp2,"-no-iso-translate ");
        }
        if (masterparam.opt[16]) {
                strcat(tmp2,"-joliet-long ");
        }
        if (masterparam.opt[17]) {
                strcat(tmp2,"-allow-lowercase ");
        }
        if (masterparam.opt[18]) {
                strcat(tmp2,"-allow-multidot ");
        }
        if (masterparam.opt[19]) {
                strcat(tmp2,"-max-iso9660-filenames ");
        }
        if (masterparam.opt[20]) {
                strcat(tmp2,"-relaxed-filenames ");
        }
        if (masterparam.opt[21]) {
                strcat(tmp2,"-dvd-video ");
        }
        if (masterparam.opt[23] > 0) {
		g_snprintf(tmptmp3,MAXLINE,"-iso-level %d ", 
			masterparam.opt[23]);
                strcat(tmp2,tmptmp3);
        }

	if (printsize) {
		strcat(tmp2,"-print-size ");
	}

	/* extract bootable stuff */
	strcpy(tmp3,"");

	if (masterparam.bootable == 1) {
		/* el torito */
		if (masterparam.boot_image && masterparam.boot_catalog) {
			strcpy(tmptmp, masterparam.boot_image);
			strcpy(tmptmp2, masterparam.boot_catalog);
			g_snprintf(tmp3,MAXLINE,"-b \"%s\" -c \"%s\"",
				convert_escape(tmptmp),
				convert_escape(tmptmp2));

			switch(masterparam.boot_type) {
			case 1:
				strcat(tmp3," -hard-disk-boot");
				break;
			case 2:
				strcat(tmp3," -no-emul-boot");
				if (masterparam.boot_load_size >= 0) {
					g_snprintf(tmptmp,MAXLINE," -boot-load-size %d", masterparam.boot_load_size);
					strcat(tmp3, tmptmp);	
				}	
				break;
			case 3:
				strcat(tmp3," -no-boot");
				break;
			default:
				break;
			}	

			if (masterparam.boot_info_table) {
				strcat(tmp3," -boot-info-table");
			}
		}
	}
	if (masterparam.bootable == 2) {
		/* sparc boot */
		if (masterparam.sparc_boot) {
			strcpy(tmptmp, masterparam.sparc_boot);
			g_snprintf(tmp3,MAXLINE,"-B \"%s\"",
				convert_escape(tmptmp));
		}
	}

	/* multisession info */
	strcpy(tmp35,"");

	if (curset.multisession == 1) {
		if (masterparam.next_session_start != -1) {

			if (convert_devnr2busid(curset.writer_devnr,tmp34) != 0) {
				g_error("failed to get devicenr for writer in build_mkisofs_cmdline\n");
			}
	
			/* appending a session to an audio CD? */
			if (cdinfo.nr_tracks > 0 && trackinfo[0]->type == 1) {
				/* don't import old session */
				g_snprintf(tmp35,MAXLINE,"-C %d,%d ",
					masterparam.last_session_start,
					masterparam.next_session_start);
			} else {
			        g_snprintf(tmp35,MAXLINE,"-C %d,%d -M \"%s\"",
					masterparam.last_session_start,
					masterparam.next_session_start,
					tmp34);	
			}
		}
	}

	/* extract headers */
	strcpy(tmp4,"");

	if (masterparam.volid) {
		strcpy(tmptmp, masterparam.volid);
		g_snprintf(tmp5,MAXLINE,"-V \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp4,tmp5);
	}	
	if (masterparam.publisher) {
		strcpy(tmptmp, masterparam.publisher);
		g_snprintf(tmp5,MAXLINE,"-publisher \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp4,tmp5);
	}	
	if (masterparam.preparer) {
		strcpy(tmptmp, masterparam.preparer);
		g_snprintf(tmp5,MAXLINE,"-preparer \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp4,tmp5);
	}	
	/* *** currently not used! ***
	if (masterparam.application) {
		strcpy(tmptmp, masterparam.application);
		g_snprintf(tmp5,MAXLINE,"-A \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp4,tmp5);
	}	
	*/
	if (masterparam.abstract) {
		strcpy(tmptmp, masterparam.abstract);
		g_snprintf(tmp5,MAXLINE,"-abstract \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp4,tmp5);
	}	
	if (masterparam.biblio) {
		strcpy(tmptmp, masterparam.biblio);
		g_snprintf(tmp5,MAXLINE,"-biblio \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp4,tmp5);
	}	
	if (masterparam.copyright) {
		strcpy(tmptmp, masterparam.copyright);
		g_snprintf(tmp5,MAXLINE,"-copyright \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp4,tmp5);
	}	

	/* now the excluded directories
	   write to temporary file */
	strcpy(tmp6,"");

	fd = fopen(master_fname2,"w");
	if (!fd) {
		g_warning("failed to open %s for writing! aborting...\n",
			master_fname2);
		strcpy(ret,"false");
		return;
	}

	loop = g_list_first(masterparam.exclude_paths);
        while (loop) {
		p = (gchar *)loop->data;
		if (p) {
			strcpy(tmptmp,p);
			g_snprintf(tmp5,MAXLINE,"%s\n",
				convert_escape(tmptmp));
			fputs(tmp5,fd);
		}
                loop = loop->next;
        }
	fclose(fd);

	/* if we actually got excludes, add the tmp-excl file */
	if (g_list_length(masterparam.exclude_paths) != 0) {
		strcpy(tmptmp,master_fname2);
		g_snprintf(tmp5,MAXLINE,"-exclude-list \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp6,tmp5);
	}

	/* output-file given? */
	if (outfile) {
		strcpy(tmptmp,outfile);
		g_snprintf(tmp5,MAXLINE,"-o \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp6,tmp5);
	}
	
	/* the last are the master-paths with redirects */
	strcpy(tmp7,"");

	fd = fopen(master_fname1,"w");
	if (!fd) {
		g_warning("failed to open %s for writing! aborting...\n",
			master_fname1);
		strcpy(ret,"false");
		return;
	}

	/* the first path is to be added on the command line
	   - all others to temporary file */
	pathcount = 0;
        loop = g_list_first(masterparam.mstr_redir);
        while (loop) {
                mstr = (mstr_redirect_t *) loop->data;
		if (mstr) {
                        if (mstr->mstr_path && !mstr->redir_path) {
				/* no redir available */
				strcpy(tmptmp,mstr->mstr_path);
				if (pathcount == 0) {
					g_snprintf(tmp5,MAXLINE,"\"%s\" ",
						convert_escape3(tmptmp));
					strcat(tmp7,tmp5);
				} else {
					/* write to file */
					g_snprintf(tmp5,MAXLINE,"%s\n",
						convert_escape2(tmptmp));
					fputs(tmp5,fd);
				}
			}
			if (mstr->mstr_path && mstr->redir_path) {
				strcpy(tmptmp,mstr->mstr_path);
				strcpy(tmptmp2,mstr->redir_path);
				if (pathcount == 0) {
					g_snprintf(tmp5,MAXLINE,"\"%s=%s\" ",
						convert_escape3(tmptmp2),
						convert_escape3(tmptmp));
					strcat(tmp7,tmp5);
				} else {
					/* write to file */
					g_snprintf(tmp5,MAXLINE,"%s=%s\n",
						convert_escape2(tmptmp2),
						convert_escape2(tmptmp));
					fputs(tmp5,fd);
				}
			}
			pathcount++;
		}
                loop = loop->next;
        }
	fclose(fd);

	/* if we got more than one path add master-file */
	if (g_list_length(masterparam.mstr_redir) > 1) {
		strcpy(tmptmp,master_fname1);
		g_snprintf(tmp5,MAXLINE,"-path-list \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp6,tmp5);
	}

	/* now put all together */
	g_snprintf(line,MAXLINE*10, "%s %s %s %s %s %s -graft-points %s", 
		tmp, tmp2, tmp3, tmp35, tmp4, tmp6, tmp7);

	strcpy(ret,line);
}


/*
 * get output of mkisofs
 */
static gboolean read_mkisofs_out(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];

	if (cond & G_IO_IN) {
		read_io_channel_all(source, line, MAXLINE);

		/* nothing read - nonblock */
		if (strcmp(line, "") == 0) {
			wait_and_process_events();
			return (TRUE);
		}

	dodebug(10, "mkisofs (stdout): %s\n", line);

	strcpy(tmp,line);
	strcat(tmp,"\n");

	append_to_text_view(readtrack_textview, tmp);
    }

	/* finished? */
	if (cond == G_IO_HUP) {

		/* pick up return status of child */
		read_done = get_wait_ret(-1);
		return (FALSE);
	}
	return (TRUE);
}


/*
 * get output of mkisofs (stderr)
 */
static gboolean read_mkisofs_stderr(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar *p;

	read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

	dodebug(10, "mkisofs (stderr): %s\n", line);

	if (strncmp(line, "Total extents",13) == 0) {
		strcpy(tmp,line);
		p = strtok(tmp,"=");
		if (p) {
			p = strtok(NULL,"");
			if (p) {
				masterparam.session_size = atoi(p);
			}
		}
	}

	strcpy(tmp,line);
	strcat(tmp,"\n");

	append_to_text_view(readtrack_textview, tmp);
	return (TRUE);
}


/*
 * call mkisofs -print-size
 */
gint fill_mkisofs_check_info(GtkWidget *text_window) {
gchar cmd[MAXLINE*10];
gint read_out, read_err;
gchar tmp[MAXLINE];
GIOChannel *gio, *gio2;

	/* if another process running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* mark our process as running */
	read_done = 999;
	read_output_ctrl = 0;
	readerr_count = 0;

	/* be sure to overwrite old value */
	masterparam.session_size = 0;

	/* save text-window */
	readtrack_textview = text_window;

	/* get commandline */
	build_mkisofs_cmdline(cmd,1,NULL,masterparam.opt[22]);

	dodebug(1, "spawning: %s\n",cmd);
	dolog(1, "Getting size of mkisofs-image\n");
	dolog(3, "Executing: %s\n", cmd);

	readcdda_pid = full_dpl_pipe3(&read_out,NULL,&read_err,cmd,0);
	readcdda_pid2 = -1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_mkisofs_out, NULL);

	gio2 = g_io_channel_unix_new(read_err);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback2 = g_io_add_watch(gio2, G_IO_IN,
                read_mkisofs_stderr, NULL);

	/* disallow delete button press */
	set_dialog_disallow_delete();

	/* now wait until get_size is finished */
	while (read_done == 999) {
		wait_and_process_events();

		/* check if somebody clicked on cancel */
		if (get_dialog_delete_pressed()) {
			clear_dialog_delete_pressed();
				kill_readcdda();
				/* mark we aborted */
				read_output_ctrl = 1;
		}
	}

	g_source_remove(readcdda_callback2);
	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);

	close(read_err);
	close(read_out);

	set_dialog_allow_delete();

	/* we killed the process */
	if (read_output_ctrl == 1) {
		strncpy(tmp, _("\nScanning aborted!\n"), MAXLINE);
		append_to_text_view(readtrack_textview, tmp);
		return 2;
	}

	return 0; 
}


/*
 * get output of mkisofs
 */
static gboolean read_master_out_io_in(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p, *p2;
gfloat pval;

	read_io_channel(source, line, MAXLINE);

	/* nothing read - nonblock */
	if (strcmp(line, "") == 0) {
		wait_and_process_events();
		return (TRUE);
	}

        dodebug(10,"mkisofs: %s\n", line);

	/* see if we finished with mastering - set to 100% */
	if (strncmp(line,"Total translation table size", 28) == 0) {
		gtk_progress_set_percentage( GTK_PROGRESS(readtrack_pbar1),
						1.0);
		set_xcdr_title(toplevel,viewmode_dialog,100);
		gtk_label_set_text(GTK_LABEL(readtrack_small_info2),
						"100%");
		/* mark we are really finished */
		read_output_ctrl = 3;
	}	

	/* we are finished with scanning directories - update infotext */
	if (read_output_ctrl == 1) {
		/* update only once */
		read_output_ctrl = 2;

		convert_kbytes2mbstring(masterparam.session_size * 2, tmp);
		g_snprintf(tmp2,MAXLINE,_("Mastering data track [%s]"), tmp);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp2);	

		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Mastering:"));	
	}


	/* see if we got a percentage value and set progress bar */
	strcpy(tmp,line);
	p = strtok(tmp,",");
	if (p != NULL) {
		p2 = strtok(NULL,"");
		if (p2 != NULL) {
			strcpy(tmp2,p2);
			strip_string(tmp2);
			if (strncmp(tmp2,"estimate finish",15) == 0) {
				/* ok..it's a percentage line */
				if (read_output_ctrl == 0) {
					read_output_ctrl = 1;
				}
				strcpy(tmp2,p);
				p = strtok(tmp2,"%");
				if (p != NULL) {
					strcpy(tmp,p);
					pval = atof(tmp);
					pval = pval/100;
					if (pval > 1.0) pval = 1.0;
					gtk_progress_set_percentage(
						GTK_PROGRESS(readtrack_pbar1),
						pval);
					set_xcdr_title(toplevel,viewmode_dialog,(gint)(pval*100+0.5));
					/* update small view */
					g_snprintf(tmp,MAXLINE,"%d%%",
						(gint)(pval*100+0.5));
					gtk_label_set_text(
					   GTK_LABEL(readtrack_small_info2),
						tmp);
					return (TRUE);
				}	
			}
		}
	}

        /* forward most other output to textview window */
        strcat(line,"\n");
	append_to_text_view(readtrack_textview, line);

	return (TRUE);
}

static gboolean read_master_out(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	if (cond & G_IO_IN) {
                read_master_out_io_in(source, cond, data);
        }

	if ((cond & G_IO_IN) && (cond & G_IO_HUP)) {
                read_io_channel_all(source, line, MAXLINE);
		if (strcmp(line, "") == 0) {
			cond = G_IO_HUP;
		} else {
			append_multiline_to_textview(line, "", readtrack_textview, "mkisofs-hup");
		}
    	}

        /* finished? */
	if (cond == G_IO_HUP) {

                /* pick up return status of child */
                read_done = get_wait_ret(-1);
		return (FALSE);
	}

        return (TRUE);
}


/*
 * call mkisofs to master a image
 */
gint start_master_action() {
char cmd[MAXLINE*10];  /* extra big buffer for very long cdrecord options */
gint read_dummy, read_err;
GIOChannel *gio, *gio2;

	/* if another write running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* mark our write-process as running */
	read_done = 999;
	read_output_ctrl = 0;
	read_abort_mark = 0;
	
	/* set info label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Scanning master directories..."));

	/* build command line */
	build_mkisofs_cmdline(cmd, 0, masterparam.image_filename, masterparam.opt[22]);

	dodebug(1, "spawning: %s\n",cmd);
	dolog(1,"Master image %s\n", masterparam.image_filename);
	dolog(3,"Executing: %s\n", cmd);

	/* yes...command line is done */
	readcdda_pid = full_dpl_pipe3(&read_dummy,NULL,&read_err,cmd,0);
	readcdda_pid2 = -1;

	gio = g_io_channel_unix_new(read_err);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_master_out, NULL);

	gio2 = g_io_channel_unix_new(read_dummy);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback2 = g_io_add_watch(gio2, G_IO_IN,
                read_cdda2wav_dummyout, NULL);

	/* now wait until write is finished */
	while (read_done == 999) {
		wait_and_process_events();
	}

	g_source_remove(readcdda_callback2);
	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);

	close(read_err);
	close(read_dummy);

	/* aborted master ? */
	if (read_abort_mark == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Mastering aborted..."));
		return 1;
	}

	/* all ok? */
	if (read_done == 0 && read_output_ctrl == 3) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Mastering successful"));
	} else {
		/* some error? */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Error mastering data track"));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Master-Error:"));	
	}

	return 0;
}


/*
 * do just swallow all output we get from stderr... and wait if the
 * process terminated
 */
static gboolean mkisofs_err(GIOChannel *source, GIOCondition cond, gpointer data) {
gchar line[MAXLINE];

	if (cond & G_IO_IN) {

	    read_io_channel_all(source, line, MAXLINE);

	    /* nothing read - nonblock */
	    if (strcmp(line, "") == 0) {
		    wait_and_process_events();
		    return (TRUE);
	    }

	    dodebug(10,"mkisofs (stderr): %s\n", line);
    	}

	if ((cond & G_IO_IN) && (cond & G_IO_HUP)) {
                read_io_channel_all(source, line, MAXLINE);
		if (strcmp(line, "") == 0) {
			cond = G_IO_HUP;
		} else {
			append_multiline_to_textview(line, "", NULL, "mkisofs-hup (stderr)");
		}
    	}

	/* readcd-finished? */
	if (cond == G_IO_HUP) {

                /* pick up return status of child */
		get_wait_ret(GPOINTER_TO_INT(data));
		dodebug(10,"Finished in mkisofs_err\n");
		return (FALSE);
	}	
	return(TRUE);
}



/*
 * call cdrecord to write a master-image on the fly
 */
gint start_onthefly_master_action(gint write_devnr) {
gchar tmp[MAXLINE];
gchar tmp3[MAXLINE];
gchar tmp4[MAXLINE];
gchar cmd[MAXLINE*10];  /* extra big buffer for very long mkisofs options */
gchar cmd2[MAXLINE*10];
gchar outcmd[MAXLINE];
gint read_out, read_err, read_err2;
gint tracknr;
pid_t pid1, pid2;
gint varirec_on, tmp_writer_speed;
gint wrmode;
GIOChannel *gio, *gio2, *gio3;

	/* if another write running, ignore */
	if (read_done == 999) {
		return -1;
	}

	/* mark our write-process as running */
	read_done = 999;
	read_output_ctrl = 0;
	pct_so_far = 0.0;
	cdrecord_reload = 0;
	cdrtimer = 0;

	strcpy(outcmd,"");
		
	/* set info label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Initializing Disc Writer..."));

	/* reset writeparams-arrays */
	g_free(writeparams.tracktype);
	g_free(writeparams.frames);
	g_free(writeparams.pct_so_far_arr);
	g_free(writeparams.pct_this_track_arr);
	writeparams.tracktype = g_new0(gint,MAXTRACKS);
	writeparams.frames = g_new0(gint,MAXTRACKS);
	writeparams.pct_so_far_arr = g_new0(gfloat,MAXTRACKS);
	writeparams.pct_this_track_arr = g_new0(gfloat,MAXTRACKS);
	writeparams.nrtracks = 0;
	writeparams.simulation = curset.writesimul;

	/* build command line of masterprocess */
	build_mkisofs_cmdline(cmd, 0, NULL, 0);

	/* build command line of write process */

	/* get bus,id,lun string */
	if (convert_devnr2busid_dev(write_devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* get driveropts */
	varirec_on = do_driveropts(tmp4, curset.writer_devnr);

	get_wrap_path("CDRECORD", tmp3);
	g_snprintf(cmd2, MAXLINE*10,
		"%s %s gracetime=%d fs=%dm -v %s ",  
		tmp3, tmp, get_gracetime(), get_cur_writer_fifo(), tmp4);

	if (get_cur_writer_drvmode() >= 0) {
		if (drivers[get_cur_writer_drvmode()] != NULL) {
			g_snprintf(tmp,MAXLINE," driver=%s", 
				drivers[get_cur_writer_drvmode()]->driver);
			strcat(cmd2,tmp);
		}
	}
	
        /* special case with varirec - always use 4x here */
        if (varirec_on) {
                tmp_writer_speed = 4;
        } else {
                tmp_writer_speed = get_cur_writer_speed();
        }

	if (get_cur_writer_speed() >= 0) {
		g_snprintf(tmp,MAXLINE," speed=%d", tmp_writer_speed);	
		strcat(cmd2, tmp);
	}

	/*
	 * allow setting of write mode only for CD
	 * as for DVD and BD solely DAO is possible
	 */
	if (curset.cdrtype < 1000 && !curset.multisession) {
		wrmode = get_cur_writemode();
		if (wrmode == 0) {
			strcat(cmd2," -dao");
		}
		if (wrmode == 1) {
			strcat(cmd2," -tao");
		}
		if (wrmode == 2) {
			strcat(cmd2," -tao defpregap=0");
		}
		if (wrmode == 3) {
			strcat(cmd2," -raw96r");
		}
		if (wrmode == 4) {
			strcat(cmd2," -raw96p");
		}
		if (wrmode == 5) {
			strcat(cmd2," -raw16");
		}
	} else if (curset.cdrtype < 1000 && curset.multisession) {
		strcat(cmd2," -tao");
	} else {
		strcat(cmd2," -dao");
	}
	if (curset.multisession) {
		if (is_a_sony(write_devnr)) {
			strcat(cmd2," -multi -data");
		} else {
			strcat(cmd2," -multi");
		}
	}
	if (curset.writesimul) {
		strcat(cmd2," -dummy");
	}
	if (curset.writeeject) {
		strcat(cmd2," -eject");
	}
	/*
	 * multisession data disc must be fixated
	 */
	if (curset.nofixate && !curset.multisession) {
		strcat(cmd2," -nofix");
	}
	if (curset.writepad) {
		strcat(cmd2," -pad");
	}
	if (curset.writeswap) {
		strcat(cmd2," -swab");
	}
	if (curset.writeoverburn) {
		strcat(cmd2," -overburn");
	}
	if (curset.writeignsize) {
		strcat(cmd2," -ignsize");
	}
	if (curset.writeimmed) {
		strcat(cmd2," -immed");
	}

	/* now add track-specification: one data track */
	g_snprintf(tmp, MAXLINE, " -waiti tsize=%ds", 
		masterparam.session_size);
	strcat(cmd2, tmp);
	strcat(cmd2," -");

	/* save command line to view in progress window */
	g_snprintf(outcmd,MAXLINE, "Calling: %s\n\n", cmd2);

	/* fill up percent values for nice progressbar movement */
	tracknr = 1;
	writeparams.tracktype[tracknr] = 0; /* data */
	writeparams.frames[tracknr] = masterparam.session_size;
	writeparams.pct_this_track_arr[tracknr] = 1.0;	
	writeparams.pct_so_far_arr[tracknr] = 0.0;
	writeparams.nrtracks = 1;

	dodebug(1, "spawning: %s | %s \n",cmd, cmd2);
	dolog(1,"Master Disc on the fly\n");
	dolog(3,"Executing: %s | %s\n", cmd, cmd2);

	/* output command to get better debug output */
	append_to_text_view(readtrack_textview, outcmd);

	/* yes...command line is done */
	full_dpl_pipe4(&pid1,&pid2,NULL,&read_err,cmd,&read_out,&read_err2,cmd2,0);
	mkisofs_pid = pid1;
	readcdda_pid = pid2;
	readcdda_pid2 = -1;

	gio = g_io_channel_unix_new(read_out);
	g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio, NULL, NULL); 
        readcdda_callback = g_io_add_watch(gio, G_IO_IN | G_IO_HUP,
                read_write_out, GINT_TO_POINTER((gint)pid2));

	gio2 = g_io_channel_unix_new(read_err2);
	g_io_channel_set_encoding(gio2, NULL, NULL); 
        readcdda_callback2 = g_io_add_watch(gio2, G_IO_IN | G_IO_HUP,
                read_write_stderr, NULL);

	gio3 = g_io_channel_unix_new(read_err);
	g_io_channel_set_flags(gio3, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding(gio3, NULL, NULL); 
        readcdda_callback3 = g_io_add_watch(gio3, G_IO_IN | G_IO_HUP,
                mkisofs_err, GINT_TO_POINTER(pid1));

	/* disallow delete button press */
	set_dialog_disallow_delete();

	cdrecord_stdin = read_err2;

	/* now wait until write is finished */
	while (read_done == 999) {
		wait_and_process_events();

		read_write_idle_loop(pid2);
	}

	g_io_channel_unref(gio);
	g_io_channel_unref(gio2);
	g_io_channel_unref(gio3);

	close(read_err2);
	close(read_err);
	close(read_out);

	set_dialog_allow_delete();
	cancel_show_dialog();

	/* no disc */
	if (read_output_ctrl == 2) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("No Disc or wrong Disc loaded."));
		return 1;
	}

	/* cannot send cue sheet */
	if (read_output_ctrl == 4) {
		show_dialog(ICO_WARN,_("Your CD-Writer did not accept the CUE sheet needed for\nDAO writing. This could mean that cdrecord does not support DAO\nwith your writer. Select TAO mode and try again."),T_OK, NULL, NULL, 0);
	}

	/* aborted write ? */
	if (read_output_ctrl == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Writing aborted..."));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Write-Error:"));
		return 1;
	}

	/* error while reading ?*/
	if (read_done != 0 || read_output_ctrl == 5) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Error writing tracks"));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Write-Error:"));
		return 1;
	} else {
		/* all ok */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("Tracks successfully written"));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),_("Successful:"));
		return 0;
	}
}


/*
 * check the version of cdrecord
 * return 1 if not correct (to old)
 */
gint check_version_cdrecord(gchar *match, gchar *found, gint *isProDVD) {
gchar line[MAXLINE];
gchar vline[MAXLINE];
gchar ver[MAXLINE];
FILE *fpin;
gchar *p;

	*isProDVD = 0;
	strcpy(ver,"");
	get_wrap_path("CDRECORD", line);
	strcat(line," -version 2>&1");

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	strcpy(line,"");
	strcpy(vline,"");
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10,"got: %s",line);

		/* only get first line */
		if (strcmp(vline,"") == 0) {
			strncpy(vline, line, MAXLINE);
		}
		if (strstr(line, "This version is an unofficial (modified) version with DVD support")) {
			dodebug(1, "WARNING: Inoffical cdrecord version found!\n", line);
			*isProDVD = 1;
		}
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	if (strcmp(vline,"") == 0 || strstr(vline,"sh:") != NULL) {
		/* failed to open - permission denied */
		return 2;
	}

	/*
	 * now line contains the version string of cdrecord
	 * try to extract the version number
	 */
	p = strstr(vline,"release");
	if (p != NULL) {
		p = strtok(p+8, " ");
		if (p != NULL) {
			strcpy(ver,p);
		}
	} else {
		p = strstr(vline, "ProDVD");
		if (p != NULL) {
			*isProDVD = 1;
			p = strtok(p, " ");
			if (p != NULL) {
			  p = strtok(NULL, " ");
			  if (p != NULL) {
				strcpy(ver,p);
			  }
			}
		}	
		if (strcmp(ver,"") == 0) {
			p = strstr(vline,"Cdrecord");
			if (p != NULL) {
				p = strtok(p+7, " ");
				if (p != NULL) {
					p = strtok(NULL, " ");
					if (p) {
						strcpy(ver,p);
					}
				}
			}
		}
	}

	/* not even a version-string found */
	if (strcmp(ver,"") == 0) {
		strcpy(found,"-unknown-");
		return 1;
	}

	/* now compare the version string */
	if (compare_versions(ver,match) >= 0) {
		strcpy(found,ver);
		return 0;
	} else {
		strcpy(found,ver);
		return 1;
	}
}

/*
 * check the version of mkisofs
 * return 1 if not correct (to old)
 */
gint check_version_mkisofs(gchar *match, gchar *found) {
gchar line[MAXLINE];
gchar vline[MAXLINE];
gchar ver[MAXLINE];
FILE *fpin;
gchar *p;

	strcpy(ver,"");
	get_wrap_path("MKISOFS", line);
	strcat(line," -version 2>/dev/null");

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	strcpy(line,"");
	strcpy(vline,"");
        for (;;) {
		if (fgets(line,MAXLINE,fpin) == NULL)
			break;
		dodebug(10,"got: %s",line);

		/* only get first line */
		if (strcmp(vline,"") == 0) {
			strncpy(vline, line, MAXLINE);
		}
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	if (strcmp(vline,"") == 0 || strstr(vline,"sh:") != NULL) {
		/* failed to open - permission denied */
		return 2;
	}

	/*
	 * now vline contains the version string of mkisofs
	 * try to extract the version number
	 */
	p = strstr(vline,"mkisofs");
	if (p != NULL) {
		p = strtok(p+8, " ");
		if (p != NULL) {
			strcpy(ver,p);
		}
	}

	/* not even a version-string found */
	if (strcmp(ver,"") == 0) {
		strcpy(found,"-unknown-");
		return 1;
	}
	
	/* now compare the version string */
	if (compare_versions(ver,match) >= 0) {
		strcpy(found,ver);
		return 0;
	} else {
		strcpy(found,ver);
		return 1;
	}
}

/*
 * check the version of cdda2wav
 * return 1 if not correct (to old)
 */
gint check_version_cdda2wav(gchar *match, gchar *found) {
gchar line[MAXLINE];
gchar vline[MAXLINE];
gchar ver[MAXLINE];
FILE *fpin;
gchar *p;

	strcpy(ver,"");
	get_wrap_path("CDDA2WAV", line);
	strcat(line," -version 2>&1");

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	strcpy(line,"");
	strcpy(vline,"");
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10,"got: %s",line);

	        /* only get first line */
        	if (strcmp(vline,"") == 0) {
                	strncpy(vline, line, MAXLINE);
        	}
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	if (strcmp(vline,"") == 0 || strstr(vline,"sh:") != NULL) {
		/* failed to open - permission denied */
		return 2;
	}

	/*
	 * now vline contains the version string of cdda2wav
	 * try to extract the version number
	 */
	p = strstr(vline,"cdda2wav");
	if (p != NULL) {
		p = strtok(p+9, " ");
		if (p != NULL) {
			strcpy(ver,p);
		}
	} else {
		p = strstr(vline,"icedax"); /* 'cdda2wav' of the outdated fork cdrkit */
		if (p != NULL) {
			p = strtok(p+7, "\n");
			if (p != NULL) {
				strcpy(ver,p);
			}
		}
	}

	/* not even a version-string found */
	if (strcmp(ver,"") == 0) {
		strcpy(found,"-unknown-");
		return 1;
	}
	
	/* now compare the version string */
	if (compare_versions(ver,match) >= 0) {
		strcpy(found,ver);
		return 0;
	} else {
		strcpy(found,ver);
		return 1;
	}
}


/*
 * check the version of readcd
 * return 1 if not correct (to old)
 */
gint check_version_readcd(gchar *match, gchar *found) {
gchar line[MAXLINE];
gchar vline[MAXLINE];
gchar ver[MAXLINE];
FILE *fpin;
gchar *p;

	strcpy(ver,"");
	get_wrap_path("READCD", line);
	strcat(line," -version 2>/dev/null");

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	strcpy(line,"");
	strcpy(vline,"");
        for (;;) {
		if (fgets(line,MAXLINE,fpin) == NULL) 
			break;
		dodebug(10,"got: %s",line);

		/* only get first line */
		if (strcmp(vline,"") == 0) {
			strncpy(vline, line, MAXLINE);
		}
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	if (strcmp(vline,"") == 0 || strstr(vline,"sh:") != NULL) {
		/* failed to open - permission denied */
		return 2;
	}

	/*
	 * now vline contains the version string of readcd
	 * try to extract the version number
	 */
	p = strstr(vline,"readcd");
	if (p != NULL) {
		p = strtok(p+7, " ");
		if (p != NULL) {
			strcpy(ver,p);
		}
	}

	/* not even a version-string found */
	if (strcmp(ver,"") == 0) {
		strcpy(found,"-unknown-");
		return 1;
	}
	
	/* now compare the version string */
	if (compare_versions(ver,match) >= 0) {
		strcpy(found,ver);
		return 0;
	} else {
		strcpy(found,ver);
		return 1;
	}
}


/*
 * check the version of the wrapper
 * return 1 if not correct
 * return 2 if user denied
 * return 3 if rootconfig unreadable
 */
gint check_version_wrapper(gchar *tmp) {
gchar line[MAXLINE];
gchar xcdrver[MAXLINE];
gchar sharever[MAXLINE];
gchar prefixver[MAXLINE];
FILE *fpin;
gint ret;
gint unreadable;

	strcpy(xcdrver,"-unknown-");
	strcpy(sharever,"-unknown-");
	strcpy(prefixver,"-unknown-");
	strncpy(line,tmp, MAXLINE);
	strcat(line," -V");
	ret = 0;
	unreadable = 0;

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	strcpy(line,"");
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10,"got: %s",line);

		if (strncmp(line,"X-CD-Roast ", 11) == 0) {
			strcpy(xcdrver,line+11);
		}
		if (strncmp(line,"sharedir: ", 10) == 0) {
			strcpy(sharever,line+10);
		}
		if (strncmp(line,"prefixdir: ", 11) == 0) {
			strcpy(prefixver,line+11);
		}
		if (strncmp(line,"Warning: rootconfig unreadable", 30) == 0) {
			unreadable = 1;
		}
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	strip_string(xcdrver);
	strip_string(sharever);
	strip_string(prefixver);

	if (strcmp(xcdrver,"ACCESS DENIED") == 0) {
		return 2;
	}
	if (unreadable) {
		return 3;
	}
	if (strcmp(xcdrver,XCDROAST_VERSION) != 0) {
		fprintf(stderr, "CRITICAL: Wrapper %s from\n\tVersion %s instead of %s\n", 
			tmp, xcdrver, XCDROAST_VERSION);
		ret = 1;
	}
	if (strcmp(sharever,sharedir) != 0) {
		fprintf(stderr, "WARNING: Wrapper %s configured\n\tsharedir %s instead of %s\n", 
			tmp, sharever, sharedir);
		ret = 0;
	}
	if (strcmp(prefixver,prefixdir) != 0) {
		fprintf(stderr, "CRITICAL: Wrapper %s configured\n\tprefixdir %s instead of %s\n", 
			tmp, prefixver, prefixdir);
		ret = 1;
	}

	return ret;
}


/*
 * check if this user is allowed to write in the given directory
 * uses the wrapper to have real conditions
 * return 0 if writeable, 1 if not
 */
gint is_dir_writeable(gchar *dir) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
FILE *fpin;
gint stat;

	get_wrap_path("WRITETEST", tmp);
	g_snprintf(line,MAXLINE,"%s \"%s\"", tmp, dir);

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
        }

	stat = pclose(fpin);
        if (stat == -1) {
                g_error("pclose error\n");
        }

	return (WEXITSTATUS(stat));
}


/*
 * run a simple non-output-command and return exit status
 */
gint simple_exec(gchar *cmd) {
gchar line[MAXLINE];
FILE *fpin;
gint stat;

	dodebug(1, "calling: %s\n", cmd);
        if ((fpin = popen(cmd,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
        }

	stat = pclose(fpin);
        if (stat == -1) {
		return -1;
        }

	return (WEXITSTATUS(stat));
}


/*
 * reads the .xinf file for a given track and writes a new .inf file
 * with the updated cd-text information
 */
void edit_xinf_for_cd_text(gchar *trackfile, gchar *tocfile) {
gchar xinfname[MAXLINE], infname[MAXLINE];
gchar tmp[MAXLINE];
gchar *p;
FILE *fd;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];
gchar title[MAXLINE], artist[MAXLINE], cddbtitle[MAXLINE];
gchar cdtitle[MAXLINE], cdartist[MAXLINE];

	/* generate xinf and inf filenames */
	strncpy(tmp,trackfile,MAXLINE - 4);
	p = rindex(tmp,'.');
	if (!p) return;
	*p = '\0';
	strcpy(xinfname,tmp);
	strcpy(infname,tmp);
	strcat(xinfname,XCDROAST_INFO_EXT);
	strcat(infname,".inf");

	dodebug(10, "reading cdtext from %s\n", xinfname);
	
	if ((fd = fopen(xinfname,"r")) == NULL) { 
		return;
	}

	strcpy(title,"");
	strcpy(artist,"");
	strcpy(cddbtitle,"");

	for (;;) {
		if (fgets(line,MAXLINE,fd) == NULL)
			break;

		/* skip empty or hashed lines */
		strip_string(line);
		if (*line == '#' || *line == '\0')
			continue;

                /* parse lines */
		if (parse_config_line(line,id,value)) {
			dodebug(10,"invalid line in infofile");
			fclose(fd);
			return;
		}	

		if (strcmp("title",id) == 0) {
			strncpy(title, value, MAXLINE);
		}
		if (strcmp("artist",id) == 0) {
			strncpy(artist, value, MAXLINE);
		}
		if (strcmp("cddb_ttitle",id) == 0) {
			strncpy(cddbtitle, value, MAXLINE);
		}
	}

	if (fclose(fd) != 0) {
		return;
	}


	/* decide if we use cddb info rather than cd-text info? */
	if (strcmp(title,"") == 0) {
		if (strcmp(cddbtitle,"") != 0) {
			/* no title, but cddb given */
			strncpy(title, cddbtitle, MAXLINE);
		}
	}

	strcpy(cdtitle,"");
	strcpy(cdartist,"");

	/* now read toc file for cd-title/artist */
	if (strcmp(tocfile,"-") != 0) {
		dodebug(10, "reading cdtext from %s\n", tocfile);

		fd = fopen(tocfile,"r");
	
		if (fd) {
			for (;;) {
				if (fgets(line,MAXLINE,fd) == NULL)
					break;

				/* skip empty or hashed lines */
				strip_string(line);
				if (*line == '#' || *line == '\0')
					continue;

                		/* parse lines */
				if (parse_config_line(line,id,value)) {
					dodebug(10,"invalid line in toc");
					fclose(fd);
					return;
				}
				if (strcmp("cdtitle",id) == 0) {
					strncpy(tmp, value, MAXLINE);
					get_artist_and_title_from_cddb(
						tmp, cdartist, cdtitle);
				}	
			}	
			fclose(fd);
		}
	}

	/* now we have read all the info from the xinf and toc file */

	/* edit the original .inf file */
	edit_xinf_for_cd_text2(infname, title, artist, cdtitle, cdartist);
}


/*
 * patch the given cd-text information the the inffile
 */
static void edit_xinf_for_cd_text2(gchar *infname, gchar *title, gchar *artist, gchar *cdtitle, gchar *cdartist) {
gchar tmpinfname[MAXLINE];
gchar tmp[MAXLINE];
gchar line[MAXLINE];
FILE *fd, *fd2;
gchar timestr[MAXLINE];
gint found, found2, found3, found4;
time_t acttime;
gchar c_title[MAXLINE];
gchar c_artist[MAXLINE];
gchar c_cdtitle[MAXLINE];
gchar c_cdartist[MAXLINE];

	dodebug(10, "cd-text info: %s, %s, %s, %s\n", title, artist, cdtitle, cdartist);	

    	/* convert title/artist/cdtitle/cdartist to ISO-8859-1, as needed for CD-Text */
    	if (!charset_convert_to_latin1(title, c_title))
        	strncpy(c_title, title, MAXLINE);
    	if (!charset_convert_to_latin1(artist, c_artist))
        	strncpy(c_artist, artist, MAXLINE);
    	if (!charset_convert_to_latin1(cdtitle, c_cdtitle))
        	strncpy(c_cdtitle, cdtitle, MAXLINE);
    	if (!charset_convert_to_latin1(cdartist, c_cdartist))
        	strncpy(c_cdartist, cdartist, MAXLINE);
    
	/* get current time */
	acttime = time((time_t *) 0);
	strcpy(timestr,ctime(&acttime));
	timestr[strlen(timestr)-1] = '\0';

	g_snprintf(tmpinfname,MAXLINE,"%s-%d", infname, (gint) getpid());	

	/* now modify the original inf file */
	fd = fopen(infname,"r");
	if (!fd) {
		/* no inf file? - generate one */
		fd2 = fopen(infname,"w");
		if (!fd2) return;
		
		/* write releveant data to new inf file */
		g_snprintf(tmp,MAXLINE,"#created by X-CD-Roast %s at %s\n", XCDROAST_VERSION, timestr);
		fputs(tmp, fd2);
		fputs("#\n",fd2);

		g_snprintf(tmp,MAXLINE,"Performer=\t'%s'\n", c_artist);
		fputs(tmp, fd2);

		g_snprintf(tmp,MAXLINE,"Albumtitle=\t'%s'\n", c_cdtitle);
		fputs(tmp, fd2);

		g_snprintf(tmp,MAXLINE,"Tracktitle=\t'%s'\n", c_title);
		fputs(tmp, fd2);

		g_snprintf(tmp,MAXLINE,"Albumperformer=\t'%s'\n", c_cdartist);
		fputs(tmp, fd2);
	
		fclose(fd2);

		dodebug(10, "created cd-text-info as %s\n", infname);
	} else {
		/* inf file openend - open now tmpfile */
		fd2 = fopen(tmpinfname,"w");
		if (!fd2) {
			fclose(fd);
			return;
		}

		found = 0;
		found2 = 0;
		found3 = 0;
		found4 = 0;
	
		for (;;) {
			if (fgets(line,MAXLINE,fd) == NULL)
				break;

			/* write modifcation date of xcdroast in new file */
			if (strncmp(line,"#created", 8) == 0) {
				fputs(line, fd2);
				g_snprintf(tmp,MAXLINE,"#modified by X-CD-Roast %s at %s\n", XCDROAST_VERSION, timestr);
				fputs(tmp, fd2);
				continue;
			}	

			/* skip old modfied lines */
			if (strncmp(line,"#modified by X-CD-Roast", 23) == 0) {
				continue;
			}

			/* replace performer */
			if (strncmp(line,"Performer=", 10) == 0) {
				g_snprintf(tmp,MAXLINE,"Performer=\t'%s'\n", c_artist);
				fputs(tmp, fd2);
				found = 1;
				continue;
			}

			/* replace title */
			if (strncmp(line,"Tracktitle=", 11) == 0) {
				g_snprintf(tmp,MAXLINE,"Tracktitle=\t'%s'\n", c_title);
				fputs(tmp, fd2);
				found2 = 1;
				continue;
			}

			/* replace albumtitle */
			if (strncmp(line,"Albumtitle=", 11) == 0) {
				g_snprintf(tmp,MAXLINE,"Albumtitle=\t'%s'\n", c_cdtitle);
				fputs(tmp, fd2);
				found3 = 1;
				continue;
			}

			/* replace albumperformer */
			if (strncmp(line,"Albumperformer=", 11) == 0) {
				g_snprintf(tmp,MAXLINE,"Albumperformer=\t'%s'\n", c_cdartist);
				fputs(tmp, fd2);
				found4 = 1;
				continue;
			}

			fputs(line,fd2);
		}

		/* could not replace all the original lines?
		   add any missing now */
		if (found == 0) {
			g_snprintf(tmp,MAXLINE,"Performer=\t'%s'\n", c_artist);
			fputs(tmp, fd2);
		}
		if (found3 == 0) {
			g_snprintf(tmp,MAXLINE,"Albumtitle=\t'%s'\n", c_cdtitle);
			fputs(tmp, fd2);
		}
		if (found2 == 0) {
			g_snprintf(tmp,MAXLINE,"Tracktitle=\t'%s'\n", c_title);
			fputs(tmp, fd2);
		}
		if (found4 == 0) {
			g_snprintf(tmp,MAXLINE,"Albumperformer=\t'%s'\n", c_cdartist);
			fputs(tmp, fd2);
		}

		fclose(fd2);
		fclose(fd);
		
		/* now move the temporary file over the original inf file */
		move_textfile(tmpinfname, infname);

		dodebug(10, "modified cd-text-info in %s\n", infname);
	}

}


/*
 * does overwrite the artist and title information in the given toc file
 */
void edit_title_artist_in_toc_file(gchar *tocfile, gchar *title, gchar *artist) {
FILE *fd, *fd2;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmptitle[MAXLINE];
gchar tmpartist[MAXLINE];
gchar tmptocfile[MAXLINE];
gint found;
struct stat buf;

	/* check first if file exists before overwriting it */
	if (lstat(tocfile, &buf) == 0) {

		/* file real and owner is also yourself? */
		if (buf.st_uid != getuid() || S_ISLNK(buf.st_mode)) {
			dodebug(1, "cannot edit tocfile %s, because file already exists and it belongs not to us, or is a link\n", tocfile);
			return;	
		}
	}

	found = 0;
	g_snprintf(tmptocfile,MAXLINE,"%s-%d", tocfile, (gint) getpid()); 

	dodebug(10, "updating toc file %s with new title/artist\n", tocfile);

	/* open toc file for reading */
        if ((fd = fopen(tocfile,"r")) == NULL) { 
		/* no file? */
                return;
        }

	/* open temporary tocfile to copy */
        if ((fd2 = fopen(tmptocfile,"w")) == NULL) { 
		/* no file? */
		fclose(fd);
                return;
        }

	/* copy all lines and change the one with the cdtitle */
        for (;;) {
                if (fgets(line,MAXLINE,fd) == NULL)
                        break;

		/* replace the line with the cdtitle */
		if (strncmp(line,"cdtitle", 7) == 0) {
			strncpy(tmptitle, title, MAXLINE);
			strncpy(tmpartist, artist, MAXLINE);
			g_snprintf(tmp,MAXLINE,"cdtitle = \"%s / %s\"\n", convert_escape(tmptitle), convert_escape(tmpartist));
			fputs(tmp, fd2);
			found = 1;
			continue;
		}

		fputs(line,fd2);
	}

	/* no line with cdtitle found? */
	if (found == 0) {
		/* add it */
		g_snprintf(tmp,MAXLINE,"cdtitle = \"%s / %s\"\n", title, artist);
		fputs(tmp, fd2);
	}
	fclose(fd2);
	fclose(fd);

        /* now move the temporary file over the original inf file */
        move_textfile(tmptocfile, tocfile);

}


/*
 * edits or creates a xinf file with updated information of title and artist
 */
void edit_title_artist_in_xinf_file(gchar *trackfile, gchar *title, gchar *artist) {
FILE *fd, *fd2;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmptitle[MAXLINE];
gchar tmpartist[MAXLINE];
gchar xinfname[MAXLINE];
gchar tmpxinfname[MAXLINE];
gint found, found2;
gchar *p;
time_t acttime;
gchar timestr[MAXLINE];

        /* get current time */
        acttime = time((time_t *) 0);
        strcpy(timestr,ctime(&acttime));
        timestr[strlen(timestr)-1] = '\0';

	found = 0;
	found2 = 0;

        strncpy(tmp,trackfile,MAXLINE - 4);
        p = rindex(tmp,'.');
        if (!p) return;
        *p = '\0';
        strcpy(xinfname,tmp);
        strcat(xinfname,XCDROAST_INFO_EXT);
        g_snprintf(tmpxinfname,MAXLINE,"%s-%d", xinfname, (gint) getpid());

	/* try to open the xinf file */
        if ((fd = fopen(xinfname,"r")) == NULL) { 
		/* no such file? try to create a new */
		fd = fopen(xinfname,"w");
		if (!fd) return;

		dodebug(10, "creating xinf-file %s with new title/artist\n", xinfname);

	        /* write header */
	        fputs("#\n",fd);
       		g_snprintf(tmp,MAXLINE,"# X-CD-Roast %s - INF-File\n",XCDROAST_VERSION);
		fputs(tmp,fd);
       		g_snprintf(tmp,MAXLINE,"# created: %s\n", timestr);
        	fputs(tmp,fd);
        	g_snprintf(tmp,MAXLINE,"# by: %s@%s\n", username, hostname);
        	fputs(tmp,fd);
        	fputs("#\n",fd);

	        g_snprintf(tmp,MAXLINE,"file = \"%s\"\n",trackfile);
	        fputs(tmp,fd);
		strncpy(tmptitle, title, MAXLINE);
	        g_snprintf(tmp,MAXLINE,"title = \"%s\"\n",convert_escape(tmptitle));
	        fputs(tmp,fd);
		strncpy(tmpartist, artist, MAXLINE);
	        g_snprintf(tmp,MAXLINE,"artist = \"%s\"\n",convert_escape(tmpartist));
     	   	fputs(tmp,fd);

		fclose(fd);
		return;
        }

        /* open temporary xinffile to copy */
        if ((fd2 = fopen(tmpxinfname,"w")) == NULL) { 
                /* no file? */
                fclose(fd);
                return;
        }

        /* copy a lines and change the one with the cdtitle */
        for (;;) {
                if (fgets(line,MAXLINE,fd) == NULL)
                        break;

                /* replace the line with the title */
                if (strncmp(line,"title", 5) == 0) {
			strncpy(tmptitle, title, MAXLINE);
                        g_snprintf(tmp,MAXLINE,"title = \"%s\"\n", convert_escape(tmptitle));
                        fputs(tmp, fd2);
                        found = 1;
                        continue;
                }

                /* replace the line with the artist */
                if (strncmp(line,"artist", 6) == 0) {
			strncpy(tmpartist, artist, MAXLINE);
                        g_snprintf(tmp,MAXLINE,"artist = \"%s\"\n", convert_escape(tmpartist)); 
                        fputs(tmp, fd2);
                        found2 = 1;
                        continue;
                }

                fputs(line,fd2);
        }

	/* lines missing? add them */
	if (found == 0) {
		strncpy(tmptitle, title, MAXLINE);
        	g_snprintf(tmp,MAXLINE,"title = \"%s\"\n", convert_escape(tmptitle));
                fputs(tmp, fd2);
	}
	if (found2 == 0) {
		strncpy(tmpartist, artist, MAXLINE);
        	g_snprintf(tmp,MAXLINE,"artist = \"%s\"\n", convert_escape(tmpartist));
        	fputs(tmp, fd2);
	}

        fclose(fd2);
        fclose(fd);

	dodebug(10, "updated xinf-file %s with new title/artist\n", xinfname);

        /* now move the temporary file over the original inf file */
        move_textfile(tmpxinfname, xinfname);
}


/*
 * will write a temporary toc file just to save the album title and performer
 * of the tracks to be written in the write-tracks menu
 */
gint create_tmp_writetracks_tocfile(gchar *tocfile) {
FILE *fd;
time_t acttime;
gchar timestr[MAXLINE];
gchar tmp[MAXLINE];
struct stat buf;

	if (tocfile == NULL) return 1;

	/* check first if file exists before overwriting it */
	if (lstat(tocfile, &buf) == 0) {

		/* file real and owner is also yourself? */
		if (buf.st_uid != getuid() || S_ISLNK(buf.st_mode)) {
			dodebug(1, "cannot create temporary tocfile %s, because file already exists and it belongs not to us, or is a link\n", tocfile);
			return 1;	
		}
	}

	dodebug(8, "creating temporary tocfile %s\n", tocfile);
	fd = fopen(tocfile,"w"); 

        if (fd == NULL) {
                g_warning("Can't open file %s for writing\n",tocfile);
                return 1;
        }

        /* get current time */
        acttime = time((time_t *) 0);
        strcpy(timestr,ctime(&acttime));
        timestr[strlen(timestr)-1] = '\0';

        /* write header */
        fputs("#\n",fd);
        g_snprintf(tmp,MAXLINE,"# X-CD-Roast %s - temporary TOC-File\n",XCDROAST_VERSION);
        fputs(tmp,fd);
        g_snprintf(tmp,MAXLINE,"# created: %s\n", timestr);
        fputs(tmp,fd);
        g_snprintf(tmp,MAXLINE,"# by: %s@%s\n", username, hostname);
        fputs(tmp,fd);
        fputs("#\n",fd);

	/* write empty cdtitle */
	g_snprintf(tmp,MAXLINE,"cdtitle = \"\"\n");
        fputs(tmp,fd);

	fclose(fd);

	return 0;
}


/*
 * removes the temporary tocfile again
 * makes plenty of security checks to remove only a "sane" file
 */
static void remove_tmp_writetracks_tocfile(gchar *tocfile) {
struct stat buf;

	/* check first if file exists before delting it */
	if (lstat(tocfile, &buf) == 0) {

		/* file real and owner is also yourself? */
		if (buf.st_uid == getuid() && !S_ISLNK(buf.st_mode)) {

			dodebug(2, "removing temporary file %s\n", tocfile);
			unlink(tocfile);	
		} else {
			g_warning("Trying to remove file %s which does not belong us or is a link!\n", tocfile);
		}
	}
}


/*
 * just read the line with the cdtitle from a tocfile and returns it
 */
void get_cdtitle_from_tmp_tocfile(gchar *tocfile, gchar *cdtitle) {
FILE *fd;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];

	strcpy(cdtitle, "");

	dodebug(2, "reading temporary toc file %s\n", tocfile);
        fd = fopen(tocfile,"r");

        if (fd == NULL) {
		return;
        }

        for (;;) {
                if (fgets(line,MAXLINE,fd) == NULL)
                        break;

                dodebug(10,"read tocfile: %s", line);
                /* skip empty or hashed lines */
                strip_string(line);
                if (*line == '#' || *line == '\0')
                        continue;

                /* parse lines */
                if (parse_config_line(line,id,value)) {
                        dodebug(10,"invalid line in tocfile");
                        fclose(fd);
                        return;
                }       

                if (strcmp("cdtitle",id) == 0) {
                        strncpy(cdtitle, value, MAXLINE);
                }
	}

	fclose(fd);
}


/*
 * saves the current list of paths to master to a file
 */
gint save_master_list(gchar *fname) {
FILE *fd;
gchar tmp[MAXLINE];
gchar tmpexcl[MAXLINE];
gchar tmppath1[MAXLINE];
gchar tmppath2[MAXLINE];
time_t acttime;
gchar timestr[MAXLINE];
mstr_redirect_t *mstr;
gchar *excl;
GList *loop;

	dodebug(1, "Opening %s for writing (master paths)\n", fname);

        fd = fopen(fname,"w"); 
        if (fd == NULL) { 
                /* error opening file */
                return 1;
        }

        /* get current time */
        acttime = time((time_t *) 0);
        strcpy(timestr,ctime(&acttime));
        timestr[strlen(timestr)-1] = '\0';

        /* write header */
        
        fputs("#\n",fd);
        g_snprintf(tmp,MAXLINE,"# X-CD-Roast %s - Master-Paths\n",XCDROAST_VERSION);
	fputs(tmp,fd);
        g_snprintf(tmp,MAXLINE,"# created: %s\n", timestr);
        fputs(tmp,fd);
        g_snprintf(tmp,MAXLINE,"# by: %s@%s\n", username, hostname);
        fputs(tmp,fd);
        fputs("#\n",fd);

	/* get all paths */
	loop = g_list_first(masterparam.mstr_redir);
	while (loop) {
                mstr = (mstr_redirect_t *) loop->data;
                if (mstr) {
			if (mstr->mstr_path && !mstr->redir_path) {
				strncpy(tmppath1, mstr->mstr_path, MAXLINE);
				g_snprintf(tmp,MAXLINE,"ADD1 = \"%s\"\n", convert_escape(tmppath1));
			} else 
			if (mstr->mstr_path && mstr->redir_path) {
				strncpy(tmppath1, mstr->mstr_path, MAXLINE);
				strncpy(tmppath2, mstr->redir_path, MAXLINE);
				g_snprintf(tmp,MAXLINE,"ADD2 = \"%s\",\"%s\"\n", convert_escape(tmppath1), convert_escape(tmppath2));
			}
        		fputs(tmp,fd);
		}
		loop = loop->next;
	}

        /* get exclude-dirs */
        loop = g_list_first(masterparam.exclude_paths);
        while (loop) {
                excl = (gchar *)loop->data;
		if (excl) {
			strncpy(tmpexcl, excl, MAXLINE);
			g_snprintf(tmp,MAXLINE,"EXCL = \"%s\"\n", convert_escape(tmpexcl));
        		fputs(tmp,fd);
		}
		loop = loop->next;
	}

        if (fclose(fd) != 0) {
                /* error closing file */
                return 1;
        }

	return 0;
}


/*
 * loads the master-paths from a file
 */
gint load_master_list(gchar *fname) {
FILE *fd;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];
gchar value2[MAXLINE];
mstr_redirect_t *mstr;

	dodebug(1, "Opening config file %s for reading (master paths)\n", fname);

        if ((fd = fopen(fname,"r")) == NULL) { 
                /* error opening file */
                return 1;
        }

	/* could open the file, now clear the master data */
	clear_mstr_glist(&masterparam.mstr_redir);
	free_glist(&masterparam.exclude_paths);


        for (;;) {
                if (fgets(line,MAXLINE,fd) == NULL)
                        break;

                dodebug(10,"master-paths: %s", line),

                /* skip empty or hashed lines */
                strip_string(line);
                if (*line == '#' || *line == '\0') 
                        continue;

                /* parse lines */
		if (parse_config_line(line,id,value)) {
                	g_warning("syntax error in master-paths-file\n");
			continue;
		}	

		if (strcmp("ADD1", id) == 0) {
			if (strcmp(value,"") != 0) {
				/* add this value to the list */
				mstr = g_new0(mstr_redirect_t,1);
				mstr->mstr_path = g_strdup(value);
				masterparam.mstr_redir = g_list_append(masterparam.mstr_redir, mstr);
			}
		}
		if (strcmp("ADD2", id) == 0) {
			parse_config_line2(line,id,value,value2); 		
			if (strcmp(value,"") != 0) {
				/* add this value to the list */
				mstr = g_new0(mstr_redirect_t,1);
				mstr->mstr_path = g_strdup(value);
				masterparam.mstr_redir = g_list_append(masterparam.mstr_redir, mstr);
				/* add add redirection if given */
				if (strcmp(value2,"") != 0) {
					add_redir_mstr_glist(
						&masterparam.mstr_redir,
						value, value2);
				}
			}
		}
		if (strcmp("EXCL", id) == 0) {
			if (strcmp(value,"") != 0) {
				/* add exclude */
				masterparam.exclude_paths = g_list_append(
                        		masterparam.exclude_paths, 
					g_strdup(value));
			}
		}
	}

        if (fclose(fd) != 0) {
                /* error closing file */
                return 1;
        }

        return 0;
}


/*
 * writes an info file with the multisession information
 * after a track was mastered
 */
gint write_inf_file_for_master(gchar *fname, gint tracknr) {
FILE *fd;
time_t acttime;
gchar timestr[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p;

	/* get current time */
	acttime = time((time_t *) 0);
	strcpy(timestr,ctime(&acttime));
	timestr[strlen(timestr)-1] = '\0';

	if (!fname || !*fname) {
		return 1;
	}

	/*
	 * generate filename:
	 * remove extension and put .xinf there
	 */
	strncpy(tmp,fname,MAXLINE -4);
	p = rindex(tmp,'.');
	if (!p) return 1;
	*p = '\0';
	strcpy(tmp2,tmp);
	strcat(tmp2,XCDROAST_INFO_EXT);

	dodebug(1,"writing inffile %s\n", tmp2);

        /* is a link? */
        if (check_islink(tmp2, NULL)) {
        	g_warning("Warning, won't overwrite a link at %s\n", tmp2);
		return 1;
        }

	/* write to file */
	fd = fopen(tmp2,"w"); 

	if (fd == NULL) {
		g_warning("Can't open file %s for writing\n",tmp2);
		return 1;
	}

	/* write header */
	fputs("#\n",fd);
	g_snprintf(tmp,MAXLINE,"# X-CD-Roast %s - INF-File\n",XCDROAST_VERSION);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"# created: %s\n", timestr);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"# by: %s@%s\n", username, hostname);
	fputs(tmp,fd);
	fputs("#\n",fd);

	g_snprintf(tmp,MAXLINE,"file = \"%s\"\n",fname);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"track = %d of %d\n",
			tracknr, tracknr);
	fputs(tmp,fd);

	g_snprintf(tmp,MAXLINE,"size = %d\n", masterparam.session_size);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"type = %d\n", 0);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"last_session_start = %d\n", masterparam.last_session_start);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"next_session_start = %d\n", masterparam.next_session_start);
	fputs(tmp,fd);

	if (fclose(fd) != 0) {
		/* error closing file */
		return 1;
	}

	return 0;
}


/*
 * Save the current write-options to a file 
 * return 0 if ok, or 1 on error
 */
gint save_writeoptions_file(gchar *confdir, gchar *fname) {
FILE *fd;
gchar tmp[MAXLINE];

        /* now check if the confdir exists */
        if (!is_directory(confdir)) {
                /* try to create directory */
                mkdir(confdir, 0700);
                dodebug(2, "trying to mkdir %s\n", confdir);
        }

	g_snprintf(tmp,MAXLINE,"%s/%s", confdir, fname);

        dodebug(1, "Opening %s for writing\n", tmp);
        dolog(3, "Saving write-options file %s\n", tmp);

        fd = fopen(tmp,"w"); 

        if (fd == NULL) { 
                /* error opening file */
                return 1;
        }

        /* write the config-file header */
        fputs("#\n",fd);
        g_snprintf(tmp,MAXLINE,"# X-CD-Roast %s Write-Options-File\n",XCDROAST_VERSION);
        fputs(tmp,fd);
        fputs("#\n",fd);
        fputs("# Automatically created by the X-CD-Roast-Setup\n",fd);
        fputs("# Don't edit! (Unless you REALLY know what you are doing)\n",fd);
        fputs("#\n\n",fd);

        /* write data */
        g_snprintf(tmp,MAXLINE,"VERSION = \"%s\"\n",XCDROAST_VERSION); 
        fputs(tmp,fd);
        g_snprintf(tmp,MAXLINE,"PLATFORM = \"%s\"\n",system_platform); 
        fputs(tmp,fd);

	g_snprintf(tmp,MAXLINE,"WRITE_SIMUL = %d\n", curset.writesimul);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_EJECT = %d\n", curset.writeeject);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_PAD = %d\n", curset.writepad);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_SWAP = %d\n", curset.writeswap);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_NOFIXATE = %d\n", curset.nofixate);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_CDTEXT = %d\n", curset.writecdtext);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_OVERBURN = %d\n", curset.writeoverburn);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_BURNFREE = %d\n", curset.writeburnfree);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_AUDIOMASTER = %d\n", curset.writeaudiomaster);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_FORCESPEED = %d\n", curset.writeforcespeed);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_VARIREC = %d\n", curset.writevarirec);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_IGNSIZE = %d\n", curset.writeignsize);
        fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITE_IMMED = %d\n", curset.writeimmed);
        fputs(tmp,fd);

        if (fclose(fd) != 0) {
                /* error closing file */
                return 1;
        }

        return 0;
}


/*
 * Load the write-options-default file  
 * return 0 if ok, or 1 on error
 */
gint load_writeoptions_file(gchar *fname) {
FILE *fd;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];

        if ((fd = fopen(fname,"r")) == NULL) { 
                /* error opening file */
        	dodebug(1, "Failed to open write options file %s\n", fname);
        	dolog(3, "Failed loading write options file %s\n", fname);
                return 1;
        }

        dodebug(1, "Opening write options file %s for reading\n", fname);
        dolog(3, "Loading write options file %s\n", fname);

        for (;;) {
                if (fgets(line,MAXLINE,fd) == NULL)
                        break;

                dodebug(10,"isooptions: %s", line),

                /* skip empty or hashed lines */
                strip_string(line);
                if (*line == '#' || *line == '\0') 
                        continue;

                /* parse lines */
		if (parse_config_line(line,id,value)) {
                	g_error("syntax error in isooptions-file\n");
		}	

		if (strcmp("WRITE_SIMUL", id) == 0) {
			curset.writesimul = atoi(value);
		}
		if (strcmp("WRITE_EJECT", id) == 0) {
			curset.writeeject = atoi(value);
		}
		if (strcmp("WRITE_PAD", id) == 0) {
			curset.writepad = atoi(value);
		}
		if (strcmp("WRITE_SWAP", id) == 0) {
			curset.writeswap = atoi(value);
		}
		if (strcmp("WRITE_NOFIXATE", id) == 0) {
			curset.nofixate = atoi(value);
		}
		if (strcmp("WRITE_CDTEXT", id) == 0) {
			curset.writecdtext = atoi(value);
		}
		if (strcmp("WRITE_OVERBURN", id) == 0) {
			curset.writeoverburn = atoi(value);
		}
		if (strcmp("WRITE_BURNFREE", id) == 0) {
			curset.writeburnfree = atoi(value);
		}
		if (strcmp("WRITE_AUDIOMASTER", id) == 0) {
			curset.writeaudiomaster = atoi(value);
		}
		if (strcmp("WRITE_FORCESPEED", id) == 0) {
			curset.writeforcespeed = atoi(value);
		}
		if (strcmp("WRITE_VARIREC", id) == 0) {
			curset.writevarirec = atoi(value);
		}
		if (strcmp("WRITE_IGNSIZE", id) == 0) {
			curset.writeignsize = atoi(value);
		}
		if (strcmp("WRITE_IMMED", id) == 0) {
			curset.writeimmed = atoi(value);
		}
	}

        if (fclose(fd) != 0) {
                /* error closing file */
                return 1;
        }

        return 0;
}


/*
 * remove the temporarary toc files
 */
void remove_tmp_xcdr_files() {
gchar tmptoc[MAXLINE];

        /* delete tmp masterfiles first */
        generate_tmp_tocfile_name(tmptoc);
        remove_tmp_writetracks_tocfile(tmptoc);
        if (master_fname1) {
                remove_tmp_writetracks_tocfile(master_fname1);
                g_free(master_fname1);
                master_fname1 = NULL;
        }
        if (master_fname2) {
                remove_tmp_writetracks_tocfile(master_fname2);
                g_free(master_fname2);
                master_fname2 = NULL;
        }
}


/*
 * used on OS X only - find out the pid of the autodiskmount daemon
 * return -1 when not found
 */
gint get_autodiskmount_pid() {
gchar line[MAXLINE];
FILE *fpin;
gchar *p;
gint pid;

	g_snprintf(line,MAXLINE,"/bin/ps -ax | /usr/bin/fgrep autodiskmount");

	dodebug(1, "calling: %s\n", line);
        if ((fpin = popen(line,"r")) == NULL) {
                g_error("popen error\n");
        }

	pid = -1;
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		strip_string(line);

		/* line found that not contains grep? should be the one */
		if (!strstr(line, "grep")) {
			p = strtok(line, " \t");
			if (p && pid == -1) {
				/* save first match */
				pid = atoi(p);
			}
		}
        }

        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	return pid;
}


/*
 * run a single command with sudo and return the exitstatus.
 */
gint run_sudo_command(gchar *cmd) {
gchar line[MAXLINE], tmp[MAXLINE];
FILE *fpin;
gint stat,ret;

	g_snprintf(line, MAXLINE, "%s -S %s", SUDO_BINARY, cmd);

        if ((fpin = popen(line,"w")) == NULL) {
                g_error("popen error\n");
        }

	/* send password */
	g_snprintf(tmp, MAXLINE, "%s\n", curset.sudo_passwd);
	fputs(tmp, fpin);

	/* command should be executed as root now */

	ret = pclose(fpin);
	stat = WEXITSTATUS(ret);

        dodebug(1, "Executed command \"%s\" with sudo. (exitstatus %d)\n", cmd, stat);

	return stat;
}


/*
 * check .inf file for a given audio track for invalid ISRC and MCN entries
 * return 0 when all ok (or file not found), 1 when invalid data found
 */
gint check_valid_isrc_mcn(gchar *tname) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];
gchar *p;
FILE *fd;
gint errfound;

	errfound = 0;

	/* generate filename of .inf file */
        strncpy(tmp,tname,MAXLINE - 3);
        p = rindex(tmp,'.');
        if (!p) return 0;
        *p = '\0';
        strcpy(tmp2,tmp);
        strcat(tmp2,".inf");

	if ((fd = fopen(tmp2,"r")) == NULL) { 
		/* file not found or readable - thats ok */
		return 0;
	}

	dodebug(11, "Checking %s for invalid ISRC or MCN\n", tmp2);

        for (;;) {
                if (fgets(line,MAXLINE,fd) == NULL)
                        break;

                /* skip empty or hashed lines */
                strip_string(line);
                if (*line == '#' || *line == '\0') 
                        continue;

                if (parse_config_line(line,id,value)) {
			continue;
		}

		if (strcmp("MCN",id) == 0) {
			errfound += verify_mcn(value);
		}
		if (strcmp("ISRC",id) == 0) {
			errfound += verify_isrc(value);
		}
	}

	fclose(fd);

	if (errfound > 0) {
		/* invalid data found */
		return 1;
	} else {
		return 0;
	}
}


/*
 * comment the ISRC and MCN numbers in a given track inf file
 * return 0 if we were able to do so, 1 on error
 */
gint clear_isrc_mcn_from_inffile(gchar *tname) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmpname[MAXLINE];
gchar line[MAXLINE];
gchar *p;
FILE *fd, *fd2;
gint stat;
time_t acttime;
gchar timestr[MAXLINE];

	/* get current time */
	acttime = time((time_t *) 0);
	strcpy(timestr,ctime(&acttime));
	timestr[strlen(timestr)-1] = '\0';

	/* generate filename of .inf file */
        strncpy(tmp,tname,MAXLINE - 3);
        p = rindex(tmp,'.');
        if (!p) return 0;
        *p = '\0';
        strcpy(tmp2,tmp);
        strcat(tmp2,".inf");
	g_snprintf(tmpname,MAXLINE,"%s-%d", tmp2, (gint) getpid());

        /* try to open the inf file */
        if ((fd = fopen(tmp2,"r")) == NULL) { 
		/* no source file - thats ok */
		return 0;
	}

	/* create temporary file */
        if ((fd2 = fopen(tmpname,"w")) == NULL) { 
                fclose(fd);
		/* we couldn't create the working copy? report error */
                return 1;
        }

        /* copy a lines and change the one with ISRC and MCN */
        for (;;) {
                if (fgets(line,MAXLINE,fd) == NULL)
                        break;

		/* write modifcation date of xcdroast in new file */
		if (strncmp(line,"#created", 8) == 0) {
			fputs(line, fd2);
			g_snprintf(tmp,MAXLINE,"#modified by X-CD-Roast %s at %s\n", XCDROAST_VERSION, timestr);
			fputs(tmp, fd2);
			continue;
		}	

		/* skip old modfied lines */
		if (strncmp(line,"#modified by X-CD-Roast", 23) == 0) {
			continue;
		}

                if (strncmp(line,"ISRC=", 5) == 0) {
                        g_snprintf(tmp,MAXLINE,"# %s", line);
                        fputs(tmp, fd2);
                        continue;
                }
                if (strncmp(line,"MCN=", 4) == 0) {
                        g_snprintf(tmp,MAXLINE,"# %s", line);
                        fputs(tmp, fd2);
                        continue;
                }

		fputs(line,fd2);
	}

	/* finished modified copy */
        fclose(fd2);
        fclose(fd);

	dodebug(10, "cleared inf-file %s from invalid ISRC or MCN id\n", tmp2);

        /* now move the temporary file over the original inf file */
        stat = move_textfile(tmpname, tmp2);

	if (stat == 1) {
		/* remove temporary file if move failed */
		unlink(tmpname);
	}

	return stat;
}


/*
 * exec a command and return its exit status
 */
gint exec_sudo(gchar *cmd) {
gchar tmp[MAXLINE];
FILE *fpin;
gint stat;

	dodebug(1, "calling: %s\n", cmd);
        if ((fpin = popen(cmd,"r")) == NULL) {
                g_error("popen error\n");
        }

        for (;;) {
                if (fgets(tmp,MAXLINE,fpin) == NULL) 
                        break;
        }

	stat = pclose(fpin);
        if (stat == -1) {
                g_error("pclose error\n");
        }

	return (WEXITSTATUS(stat));
}


/*
 * append text at a text view widget
 */
void append_to_text_view(GtkWidget *txt, gchar *tmp) {
GtkTextBuffer *buffer;
GtkTextIter iter;

        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
	gtk_text_buffer_get_end_iter(buffer, &iter);

	gtk_text_buffer_insert(buffer, &iter, tmp, -1);
	scroll_to_text_end(txt);
}


/*
 * append text at a text view widget and make it bold
 */
void append_to_text_view_bold(GtkWidget *txt, gchar *tmp) {
GtkTextBuffer *buffer;
GtkTextIter iter;
GtkTextTagTable *table;

        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
	gtk_text_buffer_get_end_iter(buffer, &iter);
	table = gtk_text_buffer_get_tag_table(buffer);

	/* create bold tag, if needed */
	if (gtk_text_tag_table_lookup(table, "bold") == NULL) {
        	gtk_text_buffer_create_tag(buffer, "bold", "weight",
                	PANGO_WEIGHT_BOLD, NULL);
	}

        gtk_text_buffer_insert_with_tags_by_name(buffer, &iter,
                        tmp, -1, "bold", NULL);
	scroll_to_text_end(txt);
}


/*
 * append text at a text view widget and make it colored
 */
void append_to_text_view_color(GtkWidget *txt, gchar *tmp, gchar *color) {
GtkTextBuffer *buffer;
GtkTextIter iter;
GtkTextTagTable *table;

        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
	gtk_text_buffer_get_end_iter(buffer, &iter);
	table = gtk_text_buffer_get_tag_table(buffer);

	if (gtk_text_tag_table_lookup(table, color) == NULL) {
        	gtk_text_buffer_create_tag(buffer, color, "foreground",
               	 	color, NULL);
	}

        gtk_text_buffer_insert_with_tags_by_name(buffer, &iter,
                        tmp, -1, color, NULL);
	scroll_to_text_end(txt);
}

static void scroll_to_text_end(GtkWidget *txt) {
GtkTextBuffer *buffer;
GtkTextMark *mark;
GtkTextIter iter;
	
	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
	gtk_text_buffer_get_end_iter(buffer, &iter);
	mark = gtk_text_buffer_get_insert(buffer);
	gtk_text_buffer_move_mark(buffer, mark, &iter);
	gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(txt), 
		mark, 0.0, TRUE, 0.5, 1.0);
}

/*
 * split a line by newlines,
 * trim (if wanted) a substring (from start to match) from the line
 * and output to a text view widget
 */

void append_multiline_to_textview(gchar *str, gchar *trimstr, GtkWidget *txt, gchar *debugcmd) {
gchar **arr, **ptr;
gchar *p;
gchar line[MAXLINE];
gchar outline[MAXLINE];
gchar convtmp[MAXLINE];

	arr = g_strsplit(str, "\n", -1);
	for (ptr = arr; *ptr; ptr++) {
		strncpy(outline, *ptr, MAXLINE);
		strip_string(outline);
        p = strstr(outline, trimstr);
        if (p != NULL) {
        	strncpy(line, p + strlen(trimstr), MAXLINE);
        } else {
            strncpy(line, outline, MAXLINE);
		}

		if (charset_convert_to_utf8(line, convtmp)) {
			strncpy(line, convtmp, MAXLINE);
		}

		if (txt != NULL) {
			append_to_text_view(txt, line);
		}
		dodebug(10,"%s: %s\n", debugcmd, line);

		if (txt != NULL) {
			append_to_text_view(txt, "\n");
		}
	}

	g_strfreev(arr);
}


/*
 * return the exit status of a process, or -1 on error
 */

static gint get_wait_ret(pid_t pid) {
gint status;
pid_t kpid;

	kpid = waitpid(pid, &status, 0);
	if (kpid != -1) {
	
		if (WIFEXITED(status)) {
			dodebug(11, "Process %d exited with return code: %d\n", kpid, WEXITSTATUS(status));
			return (WEXITSTATUS(status));
		}

		if (WIFSIGNALED(status)) {
			dodebug(11, "Process %d exited with Signal: %d\n", kpid, WTERMSIG(status));
			return -1;
		}	
	}

	/* error on wait */
	return -1;
}

