/*
	io.c
	All the functions that interact with hardware and subprocesses
	and file IO. 
	28.3.99 tn
*/

#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) || 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 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 gint dialog_done2, dialog_done, dialog_done3;
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;
extern GdkFont *fixedfont;
extern gint support_ontheflyaudio;

static void verify_readcd_err(gpointer pid, gint source, GdkInputCondition cond);
static void read_write_out(gpointer data, gint source, GdkInputCondition cond); 
static gint getdevicecap(gchar *dev, gint *readmax, gint *cdrmax, gint *dvdmax, gint *iscdr, gint *isdvdrom, gint *isdvdr);
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);


/* 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;
}


/* convert the scsi-type-number back to string */

#if 0
static void get_scsi_type_string(gchar *str, gint type,  gint removeable) {

	switch(type) {
	case 0:
		if (removeable == 0) {
			strcpy(str,_("Disk"));
		} else {
			strcpy(str,_("Removable Disk"));
		}
		break;
	case 1:
		strcpy(str,_("Tape"));
		break;
	case 2:
		strcpy(str,_("Printer"));
		break;
	case 3:
		strcpy(str,_("Processor/Scanner"));
		break;
	case 4:
		strcpy(str,_("WORM"));
		break;
	case 5:
		strcpy(str,_("CD-ROM"));
		break;
	case 6:
		strcpy(str,_("Scanner"));
		break;
	case 7:
		strcpy(str,_("Optical Storage"));
		break;
	case 8:
		strcpy(str,_("Juke Box"));
		break;
	case 9:
		strcpy(str,_("Communication"));
		break;
	default:
		strcpy(str,"");
		break;
	}
}
#endif


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_sectorburst() {
gint i;
	i = get_writerreaderdevs_index(curset.reader_devnr);
	return (writerreaderdevs[i]->audioread_sectorburst);
}

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_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 0
	if (strncmp(line,"devname",7) == 0 ) {
		/* set current scsibus nr */
		strcpy(tmp,line+7);
		p1=strtok(tmp,":");
		/* now tmp2 contains the current busnr as string */
		devname = strdup(p1);
		return;
	}
#endif
       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? */
	/* get_spawn_path(CDRECORD,tmp); */
	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 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 CD-ROM 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 if to 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);
		}
	}
}


/* print memory-structure with scsidevices (debug purposes) */
#if 0
static void print_scsidevices(scsi_devices_t **scsidevices) {
gint count;

	dodebug(2,"------ cdrecord scsidevices-structure -----\n");
	count = 0;
	while(scsidevices[count] != NULL) {
		dodebug(2,"devnr=%d %d:%d %s %s %s %d,%d (alt: %d) (ssize: %d)\n",
			scsidevices[count]->devnr,
			scsidevices[count]->bus,
			scsidevices[count]->id,
			scsidevices[count]->vendor,
			scsidevices[count]->model,
			scsidevices[count]->rev,
			scsidevices[count]->removable,
			scsidevices[count]->type,
			scsidevices[count]->alt_dev,
			scsidevices[count]->sector_size);
			
		count++;
	}
}
#endif

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;
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);
			convert_for_gtk2_textwidget(tmp2);
               	 	gtk_text_insert(GTK_TEXT(txt), NULL, NULL, NULL, tmp2, strlen(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);
	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 other fields */
	writerreaderdevs[count]->writer_drvmode = -1;
	writerreaderdevs[count]->writer_speed = cdrmax;
	writerreaderdevs[count]->writer_fifo = 4096;

	/* 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++;
	}

	writerreaderdevs[count]->audioread_interface = 0;
	writerreaderdevs[count]->audioread_speed = readmax;
	writerreaderdevs[count]->audioread_overlap = 0;
	writerreaderdevs[count]->audioread_sectorburst = 75;
	writerreaderdevs[count]->audioread_useparanoia = 0;
	writerreaderdevs[count]->audioread_paranoiaretries = 20;

	/* add to dialog window */
        if (txt) {
		g_snprintf(tmp2,MAXLINE,"[%s] %s %s %s\n",
			tmp, scsidev->vendor,
			scsidev->model, scsidev->rev);
		convert_for_gtk2_textwidget(tmp2);
                gtk_text_insert(GTK_TEXT(txt), NULL, NULL, NULL, tmp2, strlen(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;
}


#if (defined(__MACH__) && defined(__APPLE__)) 

/* interpret output of -inq and sort into memory structure */
/* if idx != -1 then we run in manual-device-mode */

static void parse_inq(gchar *line, gint idx, scsi_devices_t **scsidevices) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p;
gchar s_vendor[9];
gchar s_model[17];
gchar s_rev[5];
gint s_removable;
gint s_type;


	if (strncmp("Device type",line,11) == 0) {
		p = strtok(line,":");
		if (p) {
			p = strtok(NULL,"");
			if (p) {
				strcpy(tmp,p);
				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 entry now */
				scsidevices[scsicount]=g_new(scsi_devices_t,1);
				scsidevices[scsicount]->sector_size = DATASECTORSIZE;
				scsidevices[scsicount]->devnr = idx;
				scsidevices[scsicount]->alt_dev = idx;
				scsidevices[scsicount]->transport = 0;
				scsidevices[scsicount]->bus = 0;
				scsidevices[scsicount]->id = idx;
				scsidevices[scsicount]->removable = s_removable;
				scsidevices[scsicount]->type = s_type;
			}
		}
	}

	if (strncmp("Vendor_info",line,11) == 0) {
		p = strtok(line,":");
		if (p) {
			p = strtok(NULL,"");
			if (p) {
				strcpy(tmp2,p);
				extract_singlequoted(tmp2);

				strcpy(s_vendor,"        ");
				if (strlen(tmp2) <= 8) {
					strncpy(s_vendor,tmp2,strlen(tmp2));
				} else {
					/* strip if to long */
					strncpy(s_vendor,tmp2,8);
				}	
				s_vendor[8] = '\0';

				if (scsidevices[scsicount]) {
					strcpy(scsidevices[scsicount]->vendor,s_vendor);
				}
			}
		}
	}

	if (strncmp("Identifikation",line,14) == 0) {
		p = strtok(line,":");
		if (p) {
			p = strtok(NULL,"");
			if (p) {
				strcpy(tmp2,p);
				extract_singlequoted(tmp2);

				strcpy(s_model,"                ");
				if (strlen(tmp2) <= 16) {
					strncpy(s_model,tmp2,strlen(tmp2));
				} else {
					strncpy(s_model,tmp2,16);
				}
				s_model[16] = '\0';

				if (scsidevices[scsicount]) {
					strcpy(scsidevices[scsicount]->model,s_model);
				}	
			}
		}
	}

	if (strncmp("Revision",line,8) == 0) {
		p = strtok(line,":");
		if (p) {
			p = strtok(NULL,"");
			if (p) {
				strcpy(tmp2,p);
				extract_singlequoted(tmp2);

				strcpy(s_rev,"    ");
				if (strlen(tmp2) <= 4) {
					strncpy(s_rev,tmp2,strlen(tmp2));
				} else {
					strncpy(s_rev,tmp2,4);
				}
				s_rev[4] = '\0';

				if (scsidevices[scsicount]) {
					strcpy(scsidevices[scsicount]->rev,s_rev);
					scsicount++;
				}
			}
		}
	}

	if (scsicount >= MAXDEVICES) {
		g_error("Error: More than %d devices scanned\n",MAXDEVICES);
	}
}


/* does scan for a single device only via inq call (osX) */

static gint scanbus_via_inq(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," -inq 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_inq(line, newdevnr, 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;
}

#endif

/* 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];
GdkFont *boldfont;
gint doscanbus;
gint i, devnr;

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

	boldfont = get_some_font(BOLDFONT);
	doscanbus = 1;

	/* output status texts? */
	if (txt) {
		strncpy(tmp,_("Starting to scan for devices...\n"), MAXLINE);
		convert_for_gtk2_textwidget(tmp);
		gtk_text_insert(GTK_TEXT(txt), boldfont, NULL, NULL, tmp, strlen(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 (defined(__MACH__) && defined(__APPLE__)) 

	/* special handling for osX */
	/* try fixed list of devices */
	alt_scsidevices[0] = g_strdup("IOCompactDiscServices");
	alt_scsidevices[1] = g_strdup("IOCompactDiscServices/2");
	alt_scsidevices[2] = g_strdup("IOCompactDiscServices/3");
	alt_scsidevices[3] = g_strdup("IODVDServices");
	alt_scsidevices[4] = g_strdup("IODVDServices/2");
	alt_scsidevices[5] = g_strdup("IODVDServices/3");

	i = 0;
	while (alt_scsidevices[i] != NULL) {
		scanbus_via_inq(alt_scsidevices[i], 4096+i, txt);
		i++;
	}		
	
	/* trigger not to scan the bus any futher */
	doscanbus = 0;
#endif

	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);
		convert_for_gtk2_textwidget(tmp);
		gtk_text_insert(GTK_TEXT(txt), boldfont, NULL, NULL, tmp, strlen(tmp));
		wait_and_process_events();	
	}

	gdk_font_unref(boldfont);


	if (debug) print_writerreaderdevs();

}



/* scan for remote scsi devices */

void scanbus_rscsi(gchar *devstr, GtkWidget *txt) {
gchar tmp[MAXLINE];
GdkFont *boldfont;
gint found;
gint i, highest;

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

	boldfont = get_some_font(BOLDFONT);

	/* output status texts? */
	if (txt) {
		strncpy(tmp,_("Starting to scan for remote devices...\n"), MAXLINE);
		convert_for_gtk2_textwidget(tmp);
		gtk_text_insert(GTK_TEXT(txt), boldfont, NULL, NULL, tmp, strlen(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);
			convert_for_gtk2_textwidget(tmp);
			gtk_text_insert(GTK_TEXT(txt), NULL, NULL, NULL, tmp, strlen(tmp));
		}
		strncpy(tmp,_("Scan finished.\n"), MAXLINE);
		convert_for_gtk2_textwidget(tmp);
		gtk_text_insert(GTK_TEXT(txt), boldfont, NULL, NULL, tmp, strlen(tmp));
		wait_and_process_events();	
	}

	gdk_font_unref(boldfont);


	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];
GdkFont *boldfont;
gint devnr, found;

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

	boldfont = get_some_font(BOLDFONT);

	/* output status texts? */
	if (txt) {
		strncpy(tmp,_("Starting to scan for devices...\n"), MAXLINE);
		convert_for_gtk2_textwidget(tmp);
		gtk_text_insert(GTK_TEXT(txt), boldfont, NULL, NULL, tmp, strlen(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);
			convert_for_gtk2_textwidget(tmp);
			gtk_text_insert(GTK_TEXT(txt), NULL, NULL, NULL, tmp, strlen(tmp));
		}
		strncpy(tmp,_("Scan finished.\n"), MAXLINE);
		convert_for_gtk2_textwidget(tmp);
		gtk_text_insert(GTK_TEXT(txt), boldfont, NULL, NULL, tmp, strlen(tmp));
		wait_and_process_events();	
	}

	gdk_font_unref(boldfont);


	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);
}


/* call mkisofs -input-charset help */

void scancharsets() {
gchar line[MAXLINE];
FILE *fpin;
gint start,count,backcount;
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 (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);
			}
		}
        }

	tmp_types[count] = g_strdup("default");
	
        if (pclose(fpin) == -1) {
                g_error("pclose error\n");
        }

	/* now the types list is reversed, make it right */
	backcount = count;
	count = 0;
	while(backcount >= 0) {
		charset_types[count] = tmp_types[backcount];
		count++;
		backcount--;
	}
	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) {
gchar tmp[MAXLINE];
gchar line[MAXLINE];
gint tmpnr, errcount;
FILE *fpin;
gchar *p;

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

        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 (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 diskspace. 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(__MACH__) && defined(__APPLE__)) 
struct stat buf;
#endif

#if defined(sun) || defined(aix) || defined(__OpenBSD__)
gchar *audiodev;
#endif
	dsp = NULL;

#if defined(linux) || 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 dont 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(__MACH__) && defined(__APPLE__))

	/* on macosX we always should have the build-in-audio device */
       dsp = g_list_append(dsp,"Apple CoreAudio");
#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) {
#if !(defined(__MACH__) && defined(__APPLE__)) 
gchar tmp[MAXLINE];
# ifndef aix
struct stat buf;
# endif
#endif
#if defined(linux) || defined(__FreeBSD__)
gchar tmp2[MAXLINE];
#endif

	strcpy(ret,"");

#if defined(linux) || 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,"");
	fgets(str,MAXLINE,fpin); 

        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_INTERFACE = %d\n",writerreaderdevs[count]->audioread_interface);
		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_SECTORBURST = %d\n",writerreaderdevs[count]->audioread_sectorburst);
		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);


		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);
	g_snprintf(tmp,MAXLINE,"OPTION_SELECTIONMODE = %d\n",setupdata.option_selectionmode);
	fputs(tmp,fd);

	strcpy(tmp2,setupdata.ProDVDkey);
	g_snprintf(tmp,MAXLINE,"PRODVD_KEY = \"%s\"\n",convert_escape(tmp2));
	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];
gchar tmp[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;

		/* special case here to protect pro-dvd-key */
		if (strstr(line, "PRODVD_KEY") && strlen(line) > 50) {
			strncpy(tmp,line,50);
			tmp[50] = '\0';
			dodebug(10,"config: %s...\n", tmp);
		} else {
			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 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_INTERFACE",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->audioread_interface = atoi(value);
				}
			}
			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_SECTORBURST",id) == 0) {
				if (count >= 0 && writerreaderdevs[count]) {
					writerreaderdevs[count]->audioread_sectorburst = 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 (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 */
				/* (we want 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 (strcmp("OPTION_SELECTIONMODE",id) == 0) {
			setupdata.option_selectionmode = atoi(value);
		}
		if (strcmp("PRODVD_KEY",id) == 0) {
			/* dont overwrite evtuelly set value with empty key */
			if (strcmp(value,"") != 0) {
				g_free(setupdata.ProDVDkey);
				setupdata.ProDVDkey = g_strdup(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;

	/* to much 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);
	trackinfo[tocnr]->isosize = -1;

	/* reststring into tmp2 - strip to remove 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 dont 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 cd 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;
		}
		
		/* dont 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;
		}
		/* dont 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 to 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 void getcdtoc_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];


        /* read output of getcdtoc */
        n = read_line_wait(source, line, MAXLINE);

        /* getcdtoc finished */
        if (n <= 0) {
                gtk_input_remove(readcdda_callback);

                /* pick up return status of child */
                wait(&ret);

                /* tell our caller that we are done here */
                read_done = WEXITSTATUS(ret);

                return;
        }

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

	count_mmaperror += parse_toc(line);
}


/* 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];
gint ret, dvdsize, read_out;
gchar old_cdtext_title[MAXLINE];
gchar old_cdtext_artist[MAXLINE];
gint getcdtoc_timeout;

	/* 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 = gtk_timeout_add(5000,
			getcdtoc_atapi_timeout_func, NULL);
	}
	
	/* check if cd 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 */
			gtk_timeout_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 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, "");
	}

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

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

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

	fcntl(read_out, F_SETFL, O_NONBLOCK);

        readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
                (GdkInputFunction) getcdtoc_out, NULL);

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

        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 */
			cdinfo.leadout = dvdsize;
			trackinfo[0]->size = dvdsize;

			/* 1152000 is 256:00.00 which is not right */
			/* 449999 is  100:00.00 -1 frame */
/*			if (cdinfo.total_size == 1152000 || cdinfo.total_size == 1151999 || cdinfo.total_size == 449999) {
				cdinfo.total_size = dvdsize + 150;
			} */
			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 */
		gtk_timeout_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]);

	} else {

		/* needed for correct rights in nonroot mode */
		/* fix_guid(); */

		/* 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]);
		}

		/* 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]);
	} else {

		/* needed for correct rights in nonroot mode */
		/* fix_guid(); */

		/* 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]);
		}

		/* 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]);

	} else {
		/* needed for correct rights in nonroot mode */
		/* fix_guid(); */

		/* 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]); 
		} 

		/* 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]);

	} else {

		/* needed for correct rights in nonroot mode */
		/* fix_guid(); */

		/* 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]);
		}

		/* 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(__MACH__) && defined(__APPLE__)) 
gint mix;
#endif
#if defined(linux) || 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) || defined(__FreeBSD__)

	dodebug(10,"quering 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,"quering 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 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,"quering 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 tranmission 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(__MACH__) && defined(__APPLE__)) 
gint mix;
#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) || 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 tranmission 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 (btw to its child) 
   and hope that it terminates at it own */

void kill_readcdda() {

	/* stop any timers watching for cdrecord output */
	if (cdrtimer) {
		gtk_timeout_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 void read_cdda2wav_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p;
gint val, tnr, bulk;
gfloat pval, pval2;

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

	/* read output of cdda */
	n = read_line(source, line, MAXLINE);

	/* cdda-finished? */
	if (n <= 0) {
		gtk_input_remove(readcdda_callback);

		/* pick up return status of child */
		wait(&ret);

		/* tell our caller that we are done here */
		read_done = WEXITSTATUS(ret);
		return;
	}

	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");
		convert_for_gtk2_textwidget(tmp2);
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			fixedfont,NULL,NULL, tmp2, strlen(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;
	}

	/* track successfully recorded? */
	if (strlen(line) > strlen("successfully recorded")) {
		strcpy(tmp, line+strlen(line)-strlen("successfully recorded"));
		if (strcmp(tmp, "successfully recorded") == 0 ||
		    strcmp(tmp, "recorded successfully") == 0) {
			gtk_progress_set_percentage(GTK_PROGRESS(readtrack_pbar1), 1.0);
			/* look for last % */
			p = rindex(line,'%');
			if (p != NULL) {
				strcpy(tmp,p+1);
				strip_string(tmp);
			} else 
				strcpy(tmp,line);

			/* strips the 100% value from the string */
			strip_string(tmp);
			strcpy(line,tmp);	

			if (bulk == 1) {
				/* in bulkmode this means we switched tracks */
				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;
	}	
	
	/* 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;
		}
		if (strncmp(line,"overlap:min",11) == 0) {
			return;
		}
		strcat(line,"\n");
		convert_for_gtk2_textwidget(line);
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			fixedfont,NULL,NULL, line, strlen(line));
	}	
}


/* parse dummy output of cdda2wav */
/* output stdout of cdda2wav where never should any data be delivered */

static void read_cdda2wav_dummyout(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gchar line[MAXLINE];

	/* read output of cdda */
	n = read_line(source, line, MAXLINE);

	/* cdda-finished? */
	if (n <= 0) {
		gtk_input_remove(readcdda_callback2);
		return;
	}

	strcat(line,"\n");
	convert_for_gtk2_textwidget(line);
	gtk_text_insert(GTK_TEXT(readtrack_textview),
			fixedfont,NULL,NULL, line, strlen(line));
}


/* 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 tmpfname[MAXLINE];
gint read_in, read_out, read_dummy;

	/* 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 != 0) {
		strcpy(tmp4,"-i 1 -v all");
	} else {
		strcpy(tmp4,"-v toc,summary,sectors,titles");
	}


	if (get_cur_audioread_useparanoia()) {
		g_snprintf(tmp6, MAXLINE, "-paranoia -paraopts=retries=%d", get_cur_audioread_paranoiaretries());
	} else {
		strcpy(tmp6,"");
	}

	get_wrap_path("CDDA2WAV",tmp5);
	g_snprintf(cmd,MAXLINE,
		   "%s -D \"%s\" -g -O wav -t %s %s %s -P %d -n %d %s \"%s\"",
		   tmp5,tmp,tmp2,tmp3,tmp4,
		   get_cur_audioread_overlap(), get_cur_audioread_sectorburst(),
		   tmp6, 
		   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;

	/* set output to nonblocking - otherwise our callback would block */
	fcntl(read_out, F_SETFL, O_NONBLOCK); 
	fcntl(read_dummy, F_SETFL, O_NONBLOCK); 

	/* catch output of child */
	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,	
		(GdkInputFunction) read_cdda2wav_out, GINT_TO_POINTER(0));
	readcdda_callback2 = gdk_input_add(read_dummy, GDK_INPUT_READ,	
		(GdkInputFunction) read_cdda2wav_dummyout,NULL);

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

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

	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL, "\n", 1);

	/* 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 cmd[MAXLINE*10];  /* extra big buffer for very long cdrecord options */
gint read_in, read_out, read_dummy;
gint tracknr;

        /* 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 CD-Reader..."));
	strcpy(readtrack_info_string, "");

        /* reset writeparams-arrays */
	/* lets 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 != 0) {
                strcpy(tmp4,"-v all");
        } else {
                strcpy(tmp4,"-v toc,summary,sectors,titles");
        }

	if (get_cur_audioread_useparanoia()) {
		g_snprintf(tmp6, MAXLINE, "-paranoia -paraopts=retries=%d", get_cur_audioread_paranoiaretries());
	} else {
		strcpy(tmp6,"");
	}

	get_wrap_path("CDDA2WAV",tmp5);
        g_snprintf(cmd,MAXLINE,
                   "%s -D \"%s\" -g -O wav %s %s -P %d -n %d %s -B",
                   tmp5,tmp,tmp3,tmp4,
		   get_cur_audioread_overlap(), get_cur_audioread_sectorburst(),
		   tmp6);

	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;

        /* set output to nonblocking - otherwise our callback would block */
        fcntl(read_out, F_SETFL, O_NONBLOCK); 

        /* catch output of child */
        readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,     
                (GdkInputFunction) read_cdda2wav_out, GINT_TO_POINTER(1));
	readcdda_callback2 = gdk_input_add(read_dummy, GDK_INPUT_READ,	
		(GdkInputFunction) read_cdda2wav_dummyout,NULL);

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

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

        gtk_text_insert(GTK_TEXT(readtrack_textview),
                fixedfont,NULL,NULL, "\n", 1);

        /* 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 void read_readcd_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gint val;
gfloat pval, pval2;

	/* read output of readcd */
	n = read_line(source, line, MAXLINE);

	/* readcd-finished? */
	if (n <= 0) {
		gtk_input_remove(readcdda_callback);

		/* pick up return status of child */
		wait(&ret);

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

	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;
	}

	/* 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;

		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;
	}

	/* 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 disk? */
	if (strstr(line,"annot write")) {
		read_output_ctrl = 2;
	}

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

	/* forward most other output to textview-window */
	strcat(line,"\n");
	convert_for_gtk2_textwidget(line);
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL, line, strlen(line));
}


/* 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;

	/* 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;

	/* set output to nonblocking - otherwise our callback would block */
	fcntl(read_out, F_SETFL, O_NONBLOCK);  

	/* catch output of child */
	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,	
		(GdkInputFunction) read_readcd_out, NULL);

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

	close(read_out);
	close(read_in);

	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL, "\n", 1);

	/* 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 void checkmedium_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret, devnr, sectsize;
gchar tmp[MAXLINE];
gchar line[MAXLINE];
gchar *p;


        /* read output of readcd */
        n = read_line(source, line, MAXLINE);

        /* readcd finished */
        if (n <= 0) {
                gtk_input_remove(readcdda_callback);

                /* pick up return status of child */
                wait(&ret);

                /* tell our caller that we are done here */
                read_done = WEXITSTATUS(ret);

                return;
        }

	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);
                }
        }
}


/* checks if a disk 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;

	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;

        readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
                (GdkInputFunction) checkmedium_out, GINT_TO_POINTER(devnr));

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

        close(read_out);

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

	if (read_done == 0 && 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;
gint n;
gint fd;

	/* get text from textwidget */
	n = gtk_text_get_length(GTK_TEXT(txt));
	buf = gtk_editable_get_chars(GTK_EDITABLE(txt),0,n);

	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);
	if (fd < 0) {
		g_warning("Can't open file %s for writing\n",fname);
		return 1;
	}

	if (write(fd,buf,n) != n) {
		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 CDs */

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 cd 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 disk-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 disk-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 CDs */

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 cd 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 .inf 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; 
				}
			}
		}	
		convert_for_gtk2_textwidget(line);
		gtk_text_insert(GTK_TEXT(txt), 
			fixedfont,NULL,NULL, line, strlen(line));
        }

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


/* eject CD 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 CD 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) {
			gtk_text_insert(GTK_TEXT(txt), 
				fixedfont,NULL,NULL, line, strlen(line));
		}
        }

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

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


/* get output of cdrecord -msinfo */

static void read_msinfo_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gint nr1, nr2;
gchar *p;

        n = read_line(source, line, MAXLINE);

        /* finished? */
        if (n <= 0) {
                gtk_input_remove(readcdda_callback);
                gtk_input_remove(readcdda_callback2);

                /* pick up return status of child */
		wait(&ret);

                read_done = WEXITSTATUS(ret);

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

        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;
	}

	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;
		}
	}
}


/* get output of cdrecord -msinfo (stderr) */

static void read_msinfo_stderr(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];

	
	n = read_line(source, line, MAXLINE);

        /* finished? */
        if (n <= 0) {
                gtk_input_remove(readcdda_callback);
                gtk_input_remove(readcdda_callback2);

                /* pick up return status of child */
                wait(&ret);

                read_done = WEXITSTATUS(ret);

		return;
	}

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


/* 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;

        /* 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;

        fcntl(read_out, F_SETFL, O_NONBLOCK);
        fcntl(read_err, F_SETFL, O_NONBLOCK);

        readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
                (GdkInputFunction) read_msinfo_out, NULL);
        readcdda_callback2 = gdk_input_add(read_err, GDK_INPUT_READ,
                (GdkInputFunction) 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 CD-R 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");
                        }
                }

	}

        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 */

		/* now get the description of last mode to ensure
		   we dont 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 void read_blank_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];

        n = read_line(source, line, MAXLINE);

	/* finished? */
	if (n <= 0) {

		gtk_input_remove(readcdda_callback);
		gtk_input_remove(readcdda_callback2);

		/* pick up return status of child */
		wait(&ret);

		read_done = WEXITSTATUS(ret);

		/* perhaps the are-you-sure-you-want-to-abort dialog
		   is still running? remove to be sure we can continue */
		if (dialog_done == 999) {
			dialog_done = -1;
		}

		/* or cdrecord died by signal? e.g. SIGALRM */
		if (WIFSIGNALED(ret) && read_done == 0) {

			/* most likely invalid prodvd key */
			if (curset.isProDVD) {
				strncpy(tmp, "\nReceived ALARM from cdrecord-ProDVD. Aborting blank...\nInvalid ProDVD key?\n", MAXLINE);

				convert_for_gtk2_textwidget(tmp);
				gtk_text_insert(GTK_TEXT(readtrack_textview),
					fixedfont,NULL,NULL,tmp, strlen(tmp));
				read_done = 1;
			}
		}

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

	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;
                 /* if there was a fileselector running, cancel it */
                 dialog_done3 = -1;
                 return;
         }
 
	/* skip all lines that end with "seconds." */
	if (n > 8) {
		if (strstr(line, "seconds.")) {
			return;
		}
	}

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

	convert_for_gtk2_textwidget(tmp);
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL,tmp, strlen(tmp));

}


/* get output of cdrecord -blank (stderr) */

static void read_blank_stderr(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p;

	n = read_line(source, line, MAXLINE);

	/* finished? */
	if (n <= 0) {

               	gtk_input_remove(readcdda_callback);
               	gtk_input_remove(readcdda_callback2);

               	/* pick up return status of child */
               	wait(&ret);

               	read_done = WEXITSTATUS(ret);

               	/* perhaps the are-you-sure-you-want-to-abort dialog
                   is still running? remove to be sure we can continue */
               	if (dialog_done == 999) {
                       	dialog_done = -1;
               	}

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

		return;
	}

	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();
					dialog_done2 = 999;
					read_output_ctrl = 2;
				}
			}
		}
	}	

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

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

	convert_for_gtk2_textwidget(tmp);
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL,tmp, strlen(tmp));

}


/* 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;

	/* 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 CD 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;

	fcntl(read_out, F_SETFL, O_NONBLOCK);
	fcntl(read_err, F_SETFL, O_NONBLOCK);

	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
		(GdkInputFunction) read_blank_out, NULL);
	readcdda_callback2 = gdk_input_add(read_err, GDK_INPUT_READ,
		(GdkInputFunction) read_blank_stderr, NULL);

	/* 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 CD-R 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 (dialog_done2 != 999) {
			ret = show_dialog(ICO_WARN, _("Are you sure you want to abort the writing?\nAborting may damage the CD-RW and may require that you have\nto power-cycle the burner to get working again."),
				T_YES, T_NO, NULL, 1);
			if (ret == 0) {
			 	/* really abort */
				kill_readcdda();
				/* mark we aborted */
				read_output_ctrl = 1;
				dialog_done2 = 999;
			} else {
				/* not abort - undo button press */
				dialog_done2 = 999;
			}
		}
	}

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

	/* no disk */
	if (read_output_ctrl == 2) {
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			fixedfont,NULL,NULL,"\n",1);	

		strncpy(tmp6, _("No disk or wrong disk loaded."), MAXLINE);
		convert_for_gtk2_textwidget(tmp6);
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			fixedfont,NULL,NULL,tmp6,strlen(tmp6));
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			fixedfont,NULL,NULL,"\n\n",2);	
	}

	/* 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;
gint ret;

	/* 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) {
		gtk_timeout_remove(cdrtimer);
		cdrtimer = 0;

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

		/* 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);
                	waitpid(mkisofs_pid, &ret, 0);
		}

		return(FALSE);
	}

    	return(TRUE);
}


/* special case to try to get some last output from stderr after
   cdrecord terminated */

static void read_write_stderr_last(gint source) {
gint n;
gchar line[MAXLINE];
gchar tmp[MAXLINE];

	/* get as many output left in the pipe as possible */
	while (1) {
		n = read_line(source, line, MAXLINE);

		if (n <= 0) {
			/* pipe empty */
			return;
		}

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

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

		convert_for_gtk2_textwidget(tmp);
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			fixedfont,NULL,NULL,tmp, strlen(tmp));
	}
}


/* get output of cdrecord */

static void read_write_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p, *p2;
gint tnr, donemb, fullmb, fifoval, intbufval;
gfloat pval, pval2, pval3, pval4, avgspd;
static gint writecompleted;
gint cdrecord_stderr;

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

	/* finished? */
	if (n <= 0) {
		if (data) {
			/* try to get some last output from stderr */
			cdrecord_stderr = GPOINTER_TO_INT(data);
			read_write_stderr_last(cdrecord_stderr);
		}

		dodebug(10,"cdrecord (stdout): EOF\n");
		gtk_input_remove(readcdda_callback);
		gtk_input_remove(readcdda_callback2); 

		/* pick up return status of child */
		waitpid((pid_t)readcdda_pid, &ret, WNOHANG);

		read_done = WEXITSTATUS(ret);

		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 */
		if (dialog_done == 999) {
			dialog_done = -1;
		}

		/* or cdrecord died by signal? e.g. SIGALRM */
		if (WIFSIGNALED(ret) && read_done == 0) {

			/* most likely invalid prodvd key */
			if (curset.isProDVD && curset.cdrtype > 1000) {
				read_done = 2;
			}
		}

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

	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;
		/* if there was a fileselector running, cancel it */
		dialog_done3 = -1;
		return;
	}

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

	/* prodvd and beginning to write soon? */
	if (curset.isProDVD && curset.cdrtype > 1000) {
		if (strstr(line,"Last chance to quit")) {
			cdrtimer = gtk_timeout_add(1000, 
					cdrtimer_callback, NULL);
		}

		/* if we get that far, we are sure we are over the key
		   check */
		if (strstr(line,"1 second")) {
			if (cdrtimer) {
				gtk_timeout_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;
		}
	}

	/* 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;
		tnr = atoi(p);
		p = strtok(NULL," ");
		if (p == NULL) return;
		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;
			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;
				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;
					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;
							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;
		}
	}

	/* 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");

	convert_for_gtk2_textwidget(tmp);
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL,tmp, strlen(tmp));

}


static void read_write_stderr (gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p;

	n = read_line(source, line, MAXLINE);

	/* finished? */
	if (n <= 0) {
                dodebug(10,"cdrecord (stderr): EOF\n");
                gtk_input_remove(readcdda_callback);
                gtk_input_remove(readcdda_callback2);
 
                /* pick up return status of child */
                waitpid((pid_t)readcdda_pid, &ret, WNOHANG);
 
                read_done = WEXITSTATUS(ret);
 
                /* perhaps the are-you-sure-you-want-to-abort dialog
                   is still running? remove to be sure we can continue */
                if (dialog_done == 999) {
                        dialog_done = -1;
                }

		return;
	}

	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();
					dialog_done2 = 999;
					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 disk */
	if (n > 21) {
		strcpy(tmp, line+(strlen(line)-21));
		if (strcmp(tmp, "No disk / Wrong disk!") == 0) {
			/* mark we got no disk */
			read_output_ctrl = 2;
		}
	} 	

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

	convert_for_gtk2_textwidget(tmp);
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL,tmp, strlen(tmp));
}


/* display an error when useing prodvd and cdrecord aborts */

static void display_invalid_prodvdkey() {

	if (curset.isProDVD && curset.cdrtype > 1000) {
		show_dialog(ICO_WARN, _("cdrecord-ProDVD terminated.\n\nMost likely because you have entered\nan invalid key. Please check if it is correct."), T_OK, NULL, NULL, 0);
	}
}


/* 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 ret, tracknr;
gint varirec_on, tmp_writer_speed;
gint wrmode;

	/* 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 CD-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=%dk %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);
	}

	if (curset.multisession == 0) {
		wrmode = get_cur_writemode();
		if (wrmode == 0) {
			strcat(cmd," -dao");
		}
		if (wrmode == 2) {
			strcat(cmd," defpregap=0");
		}
		if (wrmode == 3) {
			strcat(cmd," -raw96r");
		}
		if (wrmode == 4) {
			strcat(cmd," -raw96p");
		}
		if (wrmode == 5) {
			strcat(cmd," -raw16");
		}
	}

	if (curset.writesimul == 1) {
		strcat(cmd," -dummy");
	}
	if (curset.writeeject == 1) {
		strcat(cmd," -eject");
	}
	if (curset.nofixate == 1) {
		strcat(cmd," -nofix");
	}
	if (curset.multisession == 1) {
		if (is_a_sony(devnr)) {
			strcat(cmd," -multi -data");
		} else {
			strcat(cmd," -multi");
		}
	}
	if (curset.writepad == 1) {
		strcat(cmd," -pad");
	}
	if (curset.writeswap == 1) {
		strcat(cmd," -swab");
	}
	if (curset.writeoverburn == 1) {
		strcat(cmd," -overburn");
	}
	if (curset.writeignsize == 1) {
		strcat(cmd," -ignsize");
	}
	if (curset.writeimmed == 1) {
		strcat(cmd," -immed");
	}
	if (curset.writecdtext == 1) {
		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 CD with %d tracks\n", writeparams.nrtracks);
	dolog(3,"Executing: %s\n", cmd);

	/* output command to get better debug output */
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL,outcmd, strlen(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;

	fcntl(read_out, F_SETFL, O_NONBLOCK);
	fcntl(read_err, F_SETFL, O_NONBLOCK);

	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
		(GdkInputFunction) read_write_out, GINT_TO_POINTER(read_err));
	readcdda_callback2 = gdk_input_add(read_err, GDK_INPUT_READ,
		(GdkInputFunction) read_write_stderr, NULL);

	/* now wait until write 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 CD-R 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 (dialog_done2 != 999) {
			ret = show_dialog(ICO_WARN, _("Are you sure you want to abort the writing?\nAborting may damage the CD-RW and may require that you have\nto power-cycle the burner to get working again."),
				T_YES, T_NO, NULL, 1);
			if (ret == 0) {
				/* really abort */
				kill_readcdda();
				/* mark we aborted */
				read_output_ctrl = 1;	
				dialog_done2 = 999;
			} else {
				/* not abort - undo button press */
				dialog_done2 = 999;
			}
		}
	}

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

	/* no disk */
	if (read_output_ctrl == 2) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("No disk or wrong disk 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:"));
		if (read_done == 2) {
			/* invalid cdrecord key - show window */
			display_invalid_prodvdkey();
		}
		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 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 != 0) {
                strcpy(tmp4,"-v all");
        } else {
                strcpy(tmp4,"-v toc,summary,sectors,titles");
        }

        get_wrap_path("CDDA2WAV",tmp5);
        g_snprintf(cmd,MAXLINE,
                   "%s -D \"%s\" %s -info-only -B", tmp5,tmp,tmp4);

        /* 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);
        }

        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 cd on the fly */
/* works currently only for cds 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 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 ret, tracknr, i;
pid_t pid1, pid2;
gint endsector, sectsize;
gint varirec_on, tmp_writer_speed;
gint wrmode;
gint datacount, audiocount;
gint isaudiocd;
gfloat per;

	/* 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 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);
		}

		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 CD-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) {

		/* 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 cd 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,"");
        	}
	        if (get_cur_audioread_useparanoia()) {
                	g_snprintf(atmp6, MAXLINE, "-paranoia -paraopts=retries=%d", get_cur_audioread_paranoiaretries());
        	} else {
                	strcpy(atmp6,"");
        	}

	        get_wrap_path("CDDA2WAV", tmp2);
	        g_snprintf(cmd,MAXLINE,
                   "%s -D \"%s\" -g -Oraw %s -P %d -n %d %s -no-infofile -B -",
                   tmp2,tmp,atmp3,
                   get_cur_audioread_overlap(), get_cur_audioread_sectorburst(),
                   atmp6);
	}


	/* 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=%dk %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);
	}
	if (curset.multisession == 0) {
		wrmode = get_cur_writemode();
		if (wrmode == 0) {
			strcat(cmd2," -dao");
		}
		if (wrmode == 2) {
			strcat(cmd2," defpregap=0");
		}
		if (wrmode == 3) {
			strcat(cmd2," -raw96r");
		}
		if (wrmode == 4) {
			strcat(cmd2," -raw96p");
		}
		if (wrmode == 5) {
			strcat(cmd2," -raw16");
		}
	}
	if (curset.writesimul == 1) {
		strcat(cmd2," -dummy");
	}
	if (curset.writeeject == 1) {
		strcat(cmd2," -eject");
	}
	if (curset.nofixate == 1) {
		strcat(cmd2," -nofix");
	}
#if 0
		/* allow multisession only for data */
		if (curset.multisession == 1) {
			if (is_a_sony(write_devnr)) {
				strcat(cmd2," -multi -data");
			} else {
				strcat(cmd2," -multi");
			}
		}
#endif
	if (curset.writepad == 1) {
		strcat(cmd2," -pad");
	}
	if (curset.writeswap == 1) {
		strcat(cmd2," -swab");
	}
	if (curset.writeoverburn == 1) {
		strcat(cmd2," -overburn");
	}
	if (curset.writeignsize == 1) {
		strcat(cmd2," -ignsize");
	}
	if (curset.writeimmed == 1) {
		strcat(cmd2," -immed");
	}
	if (curset.writecdtext == 1) {
		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 CD on the fly\n");
	dolog(3,"Executing: %s | %s\n", cmd, cmd2);

	/* output command to get better debug output */
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL,outcmd, strlen(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;

	fcntl(read_out, F_SETFL, O_NONBLOCK);
	fcntl(read_err, F_SETFL, O_NONBLOCK);
	fcntl(read_err2, F_SETFL, O_NONBLOCK);

	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
		(GdkInputFunction) read_write_out, GINT_TO_POINTER(read_err2));
	readcdda_callback2 = gdk_input_add(read_err2, GDK_INPUT_READ,
		(GdkInputFunction) read_write_stderr, NULL);
	readcdda_callback3 = gdk_input_add(read_err, GDK_INPUT_READ,
		(GdkInputFunction) verify_readcd_err, GINT_TO_POINTER(pid1));

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

		/* check if writer is waiting for reload medium */
		if (cdrecord_reload > 0) {
			show_dialog(ICO_WARN, _("Please reload the CD-R in the writer"), T_OK, NULL, NULL, -1);
			
			/* now send CR to cdrecord to let it continue */
			if (cdrecord_reload == 1) {
				if (write(read_err2, "\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) {
				dodebug(1,"Sending SIGUSR1 to cdrecord (pid: %d) after reload.\n", (gint) pid2);
				kill(pid2, SIGUSR1);
			}
			cdrecord_reload = 0;
		}

		/* check if somebody clicked on cancel */
		if (dialog_done2 != 999) {
			ret = show_dialog(ICO_WARN, _("Are you sure you want to abort the writing?\nAborting may damage the CD-RW and may require that you have\nto power-cycle the burner to get working again."),
				T_YES, T_NO, NULL, 1);
			if (ret == 0) {
				/* really abort */
				kill_readcdda();
				/* mark we aborted */
				read_output_ctrl = 1;	
				dialog_done2 = 999;
			} else {
				/* not abort - undo button press */
				dialog_done2 = 999;
			}
		}
	}

	close(read_err2);
	close(read_err);
	close(read_out);

	/* remove temporary inf files */
	if (isaudiocd) {
		remove_onthefly_inf_files();
	}

	/* no disk */
	if (read_output_ctrl == 2) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("No disk or wrong disk 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:"));
                if (read_done == 2) {
                        /* invalid cdrecord key - show window */
			display_invalid_prodvdkey();
                }
		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 disk */

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 ret;
gint wrmode;

	/* 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 CD-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);
	}
	if (curset.multisession == 0) {
		wrmode = get_cur_writemode();
		if (wrmode == 0) {
			strcat(cmd," -dao");
		}
		if (wrmode == 2) {
			strcat(cmd," defpregap=0");
		}
		if (wrmode == 3) {
			strcat(cmd," -raw96r");
		}
		if (wrmode == 4) {
			strcat(cmd," -raw96p");
		}
		if (wrmode == 5) {
			strcat(cmd," -raw16");
		}
	}
	if (curset.writesimul == 1) {
		strcat(cmd," -dummy");
	}
	if (curset.writeeject == 1) {
		strcat(cmd," -eject");
	}
	if (curset.multisession == 1) {
		strcat(cmd," -multi");
	}
        if (curset.writeignsize == 1) {
                strcat(cmd," -ignsize");
        }
        if (curset.writeimmed == 1) {
                strcat(cmd," -immed");
        }

	dodebug(1, "spawning: %s\n",cmd);
	dolog(1,"Fixate CD\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;

	fcntl(read_out, F_SETFL, O_NONBLOCK);
	fcntl(read_err, F_SETFL, O_NONBLOCK);

	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
		(GdkInputFunction) read_write_out, GINT_TO_POINTER(read_err));
	readcdda_callback2 = gdk_input_add(read_err, GDK_INPUT_READ,
		(GdkInputFunction) read_write_stderr, NULL);

	/* now wait until write 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 CD-R 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 (dialog_done2 != 999) {
			ret = show_dialog(ICO_WARN, _("Are you sure you want to abort the writing?\nAborting may damage the CD-RW and may require that you have\nto power-cycle the burner to get working again."),
				T_YES, T_NO, NULL, 1);
			if (ret == 0) {
				/* really abort */
				kill_readcdda();
				/* mark we aborted */
				read_output_ctrl = 1;	
				dialog_done2 = 999;
			} else {
				/* not abort - undo button press */
				dialog_done2 = 999;
			}
		}
	}

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

	/* no disk */
	if (read_output_ctrl == 2) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("No disk or wrong disk 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:"));
                if (read_done == 2) {
                        /* invalid cdrecord key - show window */
			display_invalid_prodvdkey();
                }
		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 void read_delete_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gint val;
gfloat pval;

	/* read output of readcd */
	n = read_line(source, line, MAXLINE);

	/* delete finished */
	if (n <= 0) {
		gtk_input_remove(readcdda_callback);

		/* pick up return status of child */
		wait(&ret);

		/* tell our caller that we are done here */
		if (WEXITSTATUS(ret) == 0 && read_output_ctrl != 100) {
			/* aborted */
			read_done = 2;
		} else {
			read_done = WEXITSTATUS(ret);
		}
		return;
	}

	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;
	}	
			
	strcat(line,"\n");
	convert_for_gtk2_textwidget(line);
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL, line, strlen(line));

}


/* does do the real deletion of some files */

static gint do_delete_files(gchar *cmd, gint count, gint start, gint all) {
gint read_out;

	/* 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;
	
	fcntl(read_out, F_SETFL, O_NONBLOCK);

	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
		(GdkInputFunction) read_delete_out, NULL);


	/* now wait until tracks are deleted */
	while (read_done == 999) {
		wait_and_process_events();
	}

	close(read_out);

	return read_done;
}


/* do delete some tracks from the HD */
/* do split the bulk in pieces of 50 files...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));
		}
		strcat(cmd,tmp2);

		count++;
		if (count%50 == 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) {
				read_done = 999;
				read_output_ctrl = 0;
			} else {
				broke = 1;
				break;
			}
		}
		loop = loop->next;
	}

	/* any parts left over? (and no error before) */
	if (count%50 != 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 verfify-tool and update sliders */

static void verify_readcd_out(gpointer pid, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gint val;
gfloat pval, pval2;

	/* read output of vrfytool  */
	n = read_line(source, line, MAXLINE);

	/* readcd-finished? */
	if (n <= 0) {
		gtk_input_remove(readcdda_callback);

		/* pick up return status of child */
		waitpid((pid_t)GPOINTER_TO_INT(pid), &ret, WNOHANG);

		/* tell our caller that we are done here */
		read_done = WEXITSTATUS(ret);
		return;
	}

	dodebug(10,"vry-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;
	}

	/* forward all other output to textview-window */
	strcat(line,"\n");
	convert_for_gtk2_textwidget(line);
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL, line, strlen(line));
}


/* do just swallow all output we get from stderr... and wait if the
   process terminated */

static void verify_readcd_err(gpointer pid, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];

	/* read output of vrfytool */
	n = read_line(source, line, MAXLINE);

	/* readcd-finished? */
	if (n <= 0) {
		gtk_input_remove(readcdda_callback3);

                /* pick up return status of child */
		waitpid((pid_t)GPOINTER_TO_INT(pid), &ret, WNOHANG);

                dodebug(10,"Finished in verify_readcd_err\n");

		return;
	}

	dodebug(10,"readcd/cdda2wav (stderr): %s\n", line);
}


/* 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;

	/* 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;

	/* set output to nonblocking - otherwise our callback would block */
	fcntl(read_out, F_SETFL, O_NONBLOCK);  
	fcntl(read_err, F_SETFL, O_NONBLOCK);  

	/* catch output of child */
	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,	
		(GdkInputFunction) verify_readcd_out, GINT_TO_POINTER(pid2));
	readcdda_callback3 = gdk_input_add(read_err, GDK_INPUT_READ,	
		(GdkInputFunction) verify_readcd_err, GINT_TO_POINTER(pid1));

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

	close(read_out);
	close(read_err);

	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL, "\n", 1);

	/* 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];
gint read_out, read_err;
pid_t pid1, pid2;

	/* 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,"");
	}

	if (get_cur_audioread_useparanoia()) {
		g_snprintf(tmp6, MAXLINE, "-paranoia -paraopts=retries=%d", get_cur_audioread_paranoiaretries());
	} else {
		strcpy(tmp6,"");
	}

	get_wrap_path("CDDA2WAV", tmp4);
	g_snprintf(cmd,MAXLINE,
		  "%s -D \"%s\" -g -H -v toc -O wav -t %s %s -P %d -n %d %s -",
		  tmp4,tmp,tmp2,tmp3,
		  get_cur_audioread_overlap(), get_cur_audioread_sectorburst(),
		  tmp6);
	
	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;

	/* set output to nonblocking - otherwise our callback would block */
	fcntl(read_out, F_SETFL, O_NONBLOCK);  
	fcntl(read_err, F_SETFL, O_NONBLOCK);  

	/* catch output of child */
	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,	
		(GdkInputFunction) verify_readcd_out, GINT_TO_POINTER(pid2));
	readcdda_callback3 = gdk_input_add(read_err, GDK_INPUT_READ,	
		(GdkInputFunction) verify_readcd_err, GINT_TO_POINTER(pid1));

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

	close(read_out);
	close(read_err);

	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL, "\n", 1);

	/* 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 void read_cddbtool_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
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 */
	n = read_line(source, line, MAXLINE);

	/* cddbtool finished */
	if (n <= 0) {
		gtk_input_remove(readcdda_callback);

		/* pick up return status of child */
		wait(&ret);

		read_done = WEXITSTATUS(ret);
		return;
	}

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

	/* got we the number of matches? */
	if (line[0] == '%') {
		strcpy(tmp,line+1);
		matchnr = atoi(tmp);
		return;
	}

	/* 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;
		}
		/* add this to the clist */
		strcpy(tmp,line);
		if (extract_quoted(tmp) != 0) 
			return;

		/* now add decoded string to clist */
		rowdata[0] = convert_for_gtk2(tmp);
		gtk_clist_append(cddb_clist,rowdata);
		return;
	}

	/* get output id of read line */
	strcpy(tmp,line);
	p = strtok(tmp,":");
	if (p == NULL) {
		return;
	}
	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;

		/* 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;
	case 8:
		/* got track-title */
		strcpy(tmp,line);
		if (extract_quoted(tmp) != 0) 
			return;

		if (mode == 0) {
			if (matchnr >= cdinfo.nr_tracks) {
				g_warning("invalid track count from cddb-server\n");
				return;
			}

			g_free(trackinfo[matchnr]->cddb_ttitle);
			trackinfo[matchnr]->cddb_ttitle = g_strdup(tmp);
		} else {
			cdtext_set_ttitle(tmp, matchnr+1);
		}
		matchnr++;
		return;
		
	/* 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);
}


/* 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 */

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;
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) {
		g_snprintf(tmp,MAXLINE,"cddb query %s %d ",
			cdinfo.cddb_discid, cdinfo.nr_tracks);
		for (i = 0; i < cdinfo.nr_tracks; i++) {
			g_snprintf(tmp2,MAXLINE,"%d ",
				trackinfo[i]->start_sec + 150);
			strcat(tmp,tmp2);
		}
		g_snprintf(tmp2,MAXLINE,"%d",cdinfo.total_size / 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;

	fcntl(read_out, F_SETFL, O_NONBLOCK);

	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
		(GdkInputFunction) read_cddbtool_out, GINT_TO_POINTER(mode));


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

	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,"OUT_CHARSET = \"%s\"\n",
		charset_types[masterparam.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("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 (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);
	/*
	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];
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 ");
                strcat(tmp2,charset_types[masterparam.charset]);
                strcat(tmp2," ");
	}
	if (masterparam.outcharset != 0) {
                strcat(tmp2,"-output-charset ");
                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) {
				/* dont 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);
	}	
	/*
	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 void read_mkisofs_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gchar line[MAXLINE];
gchar tmp[MAXLINE];

	n = read_line(source, line, MAXLINE);

	/* finished? */
	if (n <= 0) {
		return;
	}

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


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

	convert_for_gtk2_textwidget(tmp);
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL,tmp, strlen(tmp));
}


/* get output of mkisofs (stderr) */

static void read_mkisofs_stderr(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar *p;

	n = read_line(source, line, MAXLINE);

	/* finished? */
	if (n <= 0) {
		gtk_input_remove(readcdda_callback);
		gtk_input_remove(readcdda_callback2);

		/* pick up return status of child */
		wait(&ret);

		read_done = WEXITSTATUS(ret);

		return;
	}

	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");

	convert_for_gtk2_textwidget(tmp);
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL,tmp, strlen(tmp));

}


/* call mkisofs -print-size */

gint fill_mkisofs_check_info(GtkWidget *text_window) {
gchar cmd[MAXLINE*10];
gint read_out, read_err;
gchar tmp[MAXLINE];

	/* 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;

	fcntl(read_out, F_SETFL, O_NONBLOCK);
	fcntl(read_err, F_SETFL, O_NONBLOCK);

	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
		(GdkInputFunction) read_mkisofs_out, NULL);
	readcdda_callback2 = gdk_input_add(read_err, GDK_INPUT_READ,
		(GdkInputFunction) read_mkisofs_stderr, NULL);

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

		/* check if somebody clicked on cancel */
		if (dialog_done != 999) {
				kill_readcdda();
				/* mark we aborted */
				read_output_ctrl = 1;
				dialog_done = 999;
		}
	}

	close(read_err);
	close(read_out);


	/* we killed the process */
	if (read_output_ctrl == 1) {
		strncpy(tmp, _("\nScanning aborted!\n"), MAXLINE);
		convert_for_gtk2_textwidget(tmp);
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			fixedfont,NULL,NULL,tmp, strlen(tmp));
		return 2;
	}

	return 0; 
}


/* get output of mkisofs */

static void read_master_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p, *p2;
gfloat pval;

        n = read_line(source, line, MAXLINE);

        /* finished? */
        if (n <= 0) {
		gtk_input_remove(readcdda_callback);

                /* pick up return status of child */
                wait(&ret);

                read_done = WEXITSTATUS(ret);
                return;
        }

        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..its 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;
				}	
			}
		}
	}

        /* forward most other output to textview-window */
        strcat(line,"\n");
        gtk_text_insert(GTK_TEXT(readtrack_textview),
                fixedfont,NULL,NULL, line, strlen(line));

}


/* 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;

	/* 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;

	fcntl(read_dummy, F_SETFL, O_NONBLOCK);
	fcntl(read_err, F_SETFL, O_NONBLOCK);

	readcdda_callback = gdk_input_add(read_err, GDK_INPUT_READ,
		(GdkInputFunction) read_master_out, NULL);
	readcdda_callback2 = gdk_input_add(read_dummy, GDK_INPUT_READ,
		(GdkInputFunction) read_cdda2wav_dummyout, NULL);

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

	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 void mkisofs_err(gpointer pid, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gchar line[MAXLINE];

	n = read_line(source, line, MAXLINE);

	/* readcd-finished? */
	if (n <= 0) {
		gtk_input_remove(readcdda_callback3);

                /* pick up return status of child */
		waitpid((pid_t)GPOINTER_TO_INT(pid), &ret, WNOHANG);

		dodebug(10,"Finished in mkisofs_err\n");

		return;
	}	

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



/* 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 ret, tracknr;
pid_t pid1, pid2;
gint varirec_on, tmp_writer_speed;
gint wrmode;

	/* 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 CD-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=%dk -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);
	}

	/* Ignore write-mode in multisession-mode */
	if (curset.multisession == 0) {
		wrmode = get_cur_writemode();
		if (wrmode == 0) {
			strcat(cmd2," -dao");
		}
		if (wrmode == 2) {
			strcat(cmd2," defpregap=0");
		}
		if (wrmode == 3) {
			strcat(cmd2," -raw96r");
		}
		if (wrmode == 4) {
			strcat(cmd2," -raw96p");
		}
		if (wrmode == 5) {
			strcat(cmd2," -raw16");
		}
	}

	if (curset.writesimul == 1) {
		strcat(cmd2," -dummy");
	}
	if (curset.writeeject == 1) {
		strcat(cmd2," -eject");
	}
	if (curset.nofixate == 1) {
		strcat(cmd2," -nofix");
	}
	if (curset.multisession == 1) {
		if (is_a_sony(write_devnr)) {
			strcat(cmd2," -multi -data");
		} else {
			strcat(cmd2," -multi");
		}
	}
	if (curset.writepad == 1) {
		strcat(cmd2," -pad");
	}
	if (curset.writeswap == 1) {
		strcat(cmd2," -swab");
	}
	if (curset.writeoverburn == 1) {
		strcat(cmd2," -overburn");
	}
	if (curset.writeignsize == 1) {
		strcat(cmd2," -ignsize");
	}
	if (curset.writeimmed == 1) {
		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 CD on the fly\n");
	dolog(3,"Executing: %s | %s\n", cmd, cmd2);

	/* output command to get better debug output */
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		fixedfont,NULL,NULL,outcmd, strlen(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;

	fcntl(read_out, F_SETFL, O_NONBLOCK);
	fcntl(read_err, F_SETFL, O_NONBLOCK);
	fcntl(read_err2, F_SETFL, O_NONBLOCK);

	readcdda_callback = gdk_input_add(read_out, GDK_INPUT_READ,
		(GdkInputFunction) read_write_out, GINT_TO_POINTER(read_err2));
	readcdda_callback2 = gdk_input_add(read_err2, GDK_INPUT_READ,
		(GdkInputFunction) read_write_stderr, NULL);
	readcdda_callback3 = gdk_input_add(read_err, GDK_INPUT_READ,
		(GdkInputFunction) mkisofs_err, GINT_TO_POINTER(pid1));

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

		/* check if writer is waiting for reload medium */
		if (cdrecord_reload > 0) {
			show_dialog(ICO_WARN, _("Please reload the CD-R in the writer"), T_OK, NULL, NULL, -1);
			
			/* now send CR to cdrecord to let it continue */
			if (cdrecord_reload == 1) {
				if (write(read_err2, "\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) {
				dodebug(1,"Sending SIGUSR1 to cdrecord (pid: %d) after reload.\n", (gint) pid2);
				kill(pid2, SIGUSR1);
			}
			cdrecord_reload = 0;
		}

		/* check if somebody clicked on cancel */
		if (dialog_done2 != 999) {
			ret = show_dialog(ICO_WARN, _("Are you sure you want to abort the writing?\nAborting may damage the CD-RW and may require that you have\nto power-cycle the burner to get working again."),
				T_YES, T_NO, NULL, 1);
			if (ret == 0) {
				/* really abort */
				kill_readcdda();
				/* mark we aborted */
				read_output_ctrl = 1;	
				dialog_done2 = 999;
			} else {
				/* not abort - undo button press */
				dialog_done2 = 999;
			}
		}
	}

	close(read_err2);
	close(read_err);
	close(read_out);

	/* no disk */
	if (read_output_ctrl == 2) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), _("No disk or wrong disk 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:"));
                if (read_done == 2) {
                        /* invalid cdrecord key - show window */
			display_invalid_prodvdkey();
                }
		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>/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 (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;
	}

	/* check if the version is new enough to support 
	   on the fly audio */
	if (compare_versions(ver,CDRECORD_VERSION_WITH_ONTHFLYAUDIO) >= 0) {
		support_ontheflyaudio = 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 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,"");
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10,"got: %s",line);
        }

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

	if (strcmp(line,"") == 0 || strstr(line,"sh:") != NULL) {
		/* failed to open - permission denied */
		return 2;
	}


	/* now line contains the version string of mkisofs */
	/* try to extract the version number */

	p = strstr(line,"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 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,"");
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10,"got: %s",line);
        }

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

	if (strcmp(line,"") == 0 || strstr(line,"sh:") != NULL) {
		/* failed to open - permission denied */
		return 2;
	}

	/* now line contains the version string of cdda2wav */
	/* try to extract the version number */

	p = strstr(line,"version");
	if (p != NULL) {
		p = strtok(p+8, " _");
		if (p != NULL) {
			strcpy(ver,p);
		}
	} else {
		p = strstr(line,"cdda2wav");
		if (p != NULL) {
			p = strtok(p+9, " ");
			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 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,"");
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10,"got: %s",line);
        }

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

	if (strcmp(line,"") == 0 || strstr(line,"sh:") != NULL) {
		/* failed to open - permission denied */
		return 2;
	}


	/* now line contains the version string of readcd */
	/* try to extract the version number */

	p = strstr(line,"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));
}


/* return the commands to use to activate non-root-mode */
/* return 0 if all checks ok */

gint get_commands_to_change_nrm(gchar *ret, gint mode) {
gchar tmp[MAXLINE];
gchar bin[MAXLINE];
gint groupaddok;
gchar cmd_chown[MAXLINE], cmd_chmod[MAXLINE];
#if 0
gchar cmd_chgrp[MAXLINE];
nonroot_flags_t *entry;
GList *nrs_vals, *loop;
gchar tmp2[MAXLINE];
#endif

	strcpy(ret,"");
	groupaddok = 0;

	/* check if basic commands are available */
	get_chown_cmd(cmd_chown);
	if (cmd_chown[0] == '\0') {
		strcat(ret,"\nError: No chown command found");
		return 1;
	}

#if 0
	get_chgrp_cmd(cmd_chgrp);
	if (cmd_chgrp[0] == '\0') {
		strcat(ret,"\nError: No chgrp command found");
		return 1;
	}
#endif

	get_chmod_cmd(cmd_chmod);
	if (cmd_chmod[0] == '\0') {
		strcat(ret,"\nError: No chmod command found");
		return 1;
	}

	/* activate non-root-mode */
	if (mode == 0) {
#if 0
	 /* check if we need to create a group for the wrapper */
	 if (!check_group_exists(NONROOTMODEGROUP)) {
		/* we have to create a group here */
		if (stat(GROUPADD,&buf) == 0) {
			if (buf.st_mode & S_IEXEC) {
				/* groupadd exists and is an exectable */
				groupaddok = 1;
			}
		}
		if (!groupaddok) {
			g_snprintf(tmp,MAXLINE,_("Warning: No \"groupadd\" command found on your system.\n - Edit \"/etc/group\" and add a group \"%s\" manually.\n    Please try again then."), NONROOTMODEGROUP);
			strcat(ret,tmp);
			/* abort, without group its no way for us */
			return 1;
		} else {
			/* create command to create new group */
#if defined(__FreeBSD__)
			g_snprintf(tmp,MAXLINE,"%s groupadd -n %s\n", GROUPADD, NONROOTMODEGROUP);
#else
			g_snprintf(tmp,MAXLINE,"%s %s\n", GROUPADD, NONROOTMODEGROUP);
#endif
			strcat(ret,tmp);
		}	
	 }
#endif
	
	 /* set the correct permissions to the wrapper */
	 g_snprintf(bin,MAXLINE,"%s/%s", sharedir, WRAPPER);
	 g_snprintf(tmp,MAXLINE,"%s root %s\n", cmd_chown, bin);
	 strcat(ret,tmp);
#if 0
	 g_snprintf(tmp,MAXLINE,"%s %s %s\n", cmd_chgrp, NONROOTMODEGROUP, bin);
	 strcat(ret,tmp);
#endif
	 /* old nonroot mode - 2755 */
	 g_snprintf(tmp,MAXLINE,"%s 4755 %s\n", cmd_chmod, bin);
	 strcat(ret,tmp);


#if 0 
	 /* set correct permissions for cdrtools */
	 get_spawn_path(CDRECORD,bin);
	 g_snprintf(tmp,MAXLINE,"%s root %s\n", cmd_chown, bin);
	 strcat(ret,tmp);
	 g_snprintf(tmp,MAXLINE,"%s %s %s\n", cmd_chgrp, NONROOTMODEGROUP, bin);
	 strcat(ret,tmp);
	 g_snprintf(tmp,MAXLINE,"%s 4710 %s\n", cmd_chmod, bin);
	 strcat(ret,tmp);

	 get_spawn_path(CDRECORDPRODVD,bin);
	 if (is_file(bin)) {
	 	g_snprintf(tmp,MAXLINE,"%s root %s\n", cmd_chown, bin);
	 	strcat(ret,tmp);
	 	g_snprintf(tmp,MAXLINE,"%s %s %s\n", cmd_chgrp, NONROOTMODEGROUP, bin);
	 	strcat(ret,tmp);
	 	g_snprintf(tmp,MAXLINE,"%s 4710 %s\n", cmd_chmod, bin);
	 	strcat(ret,tmp);
	 }

	 get_spawn_path(MKISOFS,bin);
	 g_snprintf(tmp,MAXLINE,"%s root %s\n", cmd_chown, bin);
	 strcat(ret,tmp);
	 g_snprintf(tmp,MAXLINE,"%s %s %s\n", cmd_chgrp, NONROOTMODEGROUP, bin);
	 strcat(ret,tmp);
	 g_snprintf(tmp,MAXLINE,"%s 4710 %s\n", cmd_chmod, bin);
	 strcat(ret,tmp);

	 get_spawn_path(READCD,bin);
	 g_snprintf(tmp,MAXLINE,"%s root %s\n", cmd_chown, bin);
	 strcat(ret,tmp);
	 g_snprintf(tmp,MAXLINE,"%s %s %s\n", cmd_chgrp, NONROOTMODEGROUP, bin);
	 strcat(ret,tmp);
	 g_snprintf(tmp,MAXLINE,"%s 4710 %s\n", cmd_chmod, bin);
	 strcat(ret,tmp);

	 get_spawn_path(CDDA2WAV,bin);
	 g_snprintf(tmp,MAXLINE,"%s root %s\n", cmd_chown, bin);
	 strcat(ret,tmp);
	 g_snprintf(tmp,MAXLINE,"%s %s %s\n", cmd_chgrp, NONROOTMODEGROUP, bin);
	 strcat(ret,tmp);
	 g_snprintf(tmp,MAXLINE,"%s 4710 %s\n", cmd_chmod, bin);
	 strcat(ret,tmp);
#endif

	} else {
		/* deactivate non-root-mode */

#if 0
		nrs_vals = NULL;
		/* try to load original flags from file */
		load_nonrootvalues(configdir, NONROOTBAK, &nrs_vals);

		/* loop through all values */
		loop = g_list_first(nrs_vals);
		while(loop) {
			entry = (nonroot_flags_t *)loop->data;

			return_user_name(entry->uid, tmp2);
	 		g_snprintf(tmp,MAXLINE,"%s %s %s\n", cmd_chown,
				tmp2, entry->path);
	 		strcat(ret,tmp);
			return_group_name(entry->gid, tmp2);
	 		g_snprintf(tmp,MAXLINE,"%s %s %s\n", cmd_chgrp, 
				tmp2, entry->path);
	 		strcat(ret,tmp);
	 		g_snprintf(tmp,MAXLINE,"%s %o %s\n", cmd_chmod, 
				(gint) entry->mode, entry->path);
	 		strcat(ret,tmp);

			loop = loop->next;  
		}
		free_nonrootvalues(&nrs_vals);
#endif
	 	g_snprintf(bin,MAXLINE,"%s/%s", sharedir, WRAPPER);
	 	g_snprintf(tmp,MAXLINE,"%s 755 %s\n", cmd_chmod, bin);
	 	strcat(ret,tmp);
	}
	return 0;
}


/* save the current file owners and permissions to a file */
#if 0
gint save_current_nonrootvalues(gchar *confdir, gchar *fname) {
gchar bin[MAXLINE];
gchar tmp[MAXLINE];
gchar share[MAXLINE];
struct stat buf;
uid_t uid;
gid_t gid;
mode_t mode;
FILE *fd;
	
	/* make sharedir absolute if is not yet */
	if (sharedir[0] != '/') {
		getcwd(tmp,MAXLINE);
		g_snprintf(share,MAXLINE,"%s/%s", tmp, sharedir);	
	} else {
		strcpy(share,sharedir);
	} 

	/* first create directory to store nonrootconfig */
	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);

	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 Non-Root-Mode Configuration\n",XCDROAST_VERSION);
	fputs(tmp,fd);
	fputs("#\n",fd);
	fputs("# Contains the files with their flags before they got changed\n",fd);
	fputs("# by X-CD-Roast Non-Root-Mode activation.\n", fd);
	fputs("# Used to restore the original values when disabling Non-Root-Mode.\n",fd);
	fputs("#\n",fd);
 
	/* get details about the wrapper */
	g_snprintf(bin,MAXLINE,"%s/%s", share, WRAPPER);
	if (stat(bin,&buf) == 0) {
		uid = buf.st_uid;
		gid = buf.st_gid;
		mode = buf.st_mode & 07777;

		g_snprintf(tmp,MAXLINE,"%s: %d %d 0%o\n", bin, 
			(gint) uid, (gint) gid, (gint) mode);
		fputs(tmp,fd);
	}

	/* now the cdrtools - make path absolute */
	get_spawn_path(CDRECORD,bin);
	if (bin[0] != '/') {
		getcwd(tmp,MAXLINE);
		g_snprintf(bin2,MAXLINE,"%s/%s", tmp, bin);	
	} else {
		strcpy(bin2,bin);
	} 
	if (stat(bin2,&buf) == 0) {
		uid = buf.st_uid;
		gid = buf.st_gid;
		mode = buf.st_mode & 07777;

		g_snprintf(tmp,MAXLINE,"%s: %d %d 0%o\n", bin2, 
			(gint) uid, (gint) gid, (gint) mode);
		fputs(tmp,fd);
	}

	get_spawn_path(MKISOFS,bin);
	if (bin[0] != '/') {
		getcwd(tmp,MAXLINE);
		g_snprintf(bin2,MAXLINE,"%s/%s", tmp, bin);	
	} else {
		strcpy(bin2,bin);
	} 
	if (stat(bin2,&buf) == 0) {
		uid = buf.st_uid;
		gid = buf.st_gid;
		mode = buf.st_mode & 07777;

		g_snprintf(tmp,MAXLINE,"%s: %d %d 0%o\n", bin2, 
			(gint) uid, (gint) gid, (gint) mode);
		fputs(tmp,fd);
	}

	get_spawn_path(READCD,bin);
	if (bin[0] != '/') {
		getcwd(tmp,MAXLINE);
		g_snprintf(bin2,MAXLINE,"%s/%s", tmp, bin);	
	} else {
		strcpy(bin2,bin);
	} 
	if (stat(bin2,&buf) == 0) {
		uid = buf.st_uid;
		gid = buf.st_gid;
		mode = buf.st_mode & 07777;

		g_snprintf(tmp,MAXLINE,"%s: %d %d 0%o\n", bin2, 
			(gint) uid, (gint) gid, (gint) mode);
		fputs(tmp,fd);
	}

	get_spawn_path(CDDA2WAV,bin);
	if (bin[0] != '/') {
		getcwd(tmp,MAXLINE);
		g_snprintf(bin2,MAXLINE,"%s/%s", tmp, bin);	
	} else {
		strcpy(bin2,bin);
	} 
	if (stat(bin2,&buf) == 0) {
		uid = buf.st_uid;
		gid = buf.st_gid;
		mode = buf.st_mode & 07777;

		g_snprintf(tmp,MAXLINE,"%s: %d %d 0%o\n", bin2, 
			(gint) uid, (gint) gid, (gint) mode);
		fputs(tmp,fd);
	}
	if (fclose(fd) != 0) {
		/* error closing file */
		return 1;
	}

	return 0;
	
}
#endif 


/* 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));
}


/* load nonroot-values from file */
/* if some values not found, generate default */

#if 0

gint load_nonrootvalues(gchar *confdir, gchar *fname, GList **ret) {
gchar tmp[MAXLINE];
gchar line[MAXLINE];
gchar path[MAXLINE];
FILE *fd;
gchar *p;
gint uid, gid, mode;
gint foundwrapper, foundcdrecord, foundmkisofs, foundcdda2wav, foundreadcd;

	foundwrapper = 0;
	foundcdrecord = 0;
	foundmkisofs = 0;
	foundcdda2wav = 0;
	foundreadcd = 0;

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

	/* open file */
        if ((fd = fopen(tmp,"r")) != NULL) { 

         dodebug(1, "Opened %s for reading\n", tmp);


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

                dodebug(10,"non-root-values: %s", line),

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

		uid = -1;
		gid = -1;
		mode = -1;

		/* extract path */
		strncpy(path,line,MAXLINE);
		p = rindex(path,':');
		if (p) {
			*p = '\0';
			
			strncpy(tmp, p+1, MAXLINE);
			strip_string(tmp);
			
			/* extract uid, gid and mode */
			p = strtok(tmp," ");
			if (p) {
				uid = atoi(p);
				p = strtok(NULL," ");
				if (p) {
					gid = atoi(p);
					p = strtok(NULL,"");
					if (p) {
						mode = strtol(p, (char **)NULL,  8);
					}
				}
			}				
		} else {
			*path = '\0';
		}

		/* all data extracted successfully? */
		if (*path != '\0' && uid != -1 && gid != -1 && mode != -1) {
			
			add_to_nonrootvalues(ret, path, uid, gid, mode);

			/* check what entry we just added */
			if (strstr(path, WRAPPER)) {
				foundwrapper = 1;
			}
			if (strstr(path, CDRECORD)) {
				foundcdrecord = 1;
			}
			if (strstr(path, MKISOFS)) {
				foundmkisofs = 1;
			}
			if (strstr(path, READCD)) {
				foundreadcd = 1;
			}
			if (strstr(path, CDDA2WAV)) {
				foundcdda2wav = 1;
			}
		}

	 }

	 fclose(fd);
	}

	/* now we read the file (or not, if it was not found) and add
	   default values for any things we are missing */
	if (!foundwrapper) {
		g_snprintf(path,MAXLINE,"%s/%s", sharedir, WRAPPER);
		add_to_nonrootvalues(ret, path, 0, 0, 0755);
	}
	if (!foundcdrecord) {
		get_spawn_path(CDRECORD,path);
		add_to_nonrootvalues(ret, path, 0, 0, 0755);
	}
	if (!foundmkisofs) {
		get_spawn_path(MKISOFS,path);
		add_to_nonrootvalues(ret, path, 0, 0, 0755);
	}
	if (!foundreadcd) {
		get_spawn_path(READCD,path);
		add_to_nonrootvalues(ret, path, 0, 0, 0755);
	}
	if (!foundcdda2wav) {
		get_spawn_path(CDDA2WAV,path);
		add_to_nonrootvalues(ret, path, 0, 0, 0755);
	}

	return 0;
}
#endif 

/* 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];

/*
	g_print("tocfile: %s, file: %s\n", tocfile, trackfile);
*/

	/* 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;

	dodebug(10, "cd-text info: %s, %s, %s, %s\n", title, artist, cdtitle, cdartist);	

	/* 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", artist);
		fputs(tmp, fd2);

		g_snprintf(tmp,MAXLINE,"Albumtitle=\t'%s'\n", cdtitle);
		fputs(tmp, fd2);

		g_snprintf(tmp,MAXLINE,"Tracktitle=\t'%s'\n", title);
		fputs(tmp, fd2);

		g_snprintf(tmp,MAXLINE,"Albumperformer=\t'%s'\n", 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", artist);
				fputs(tmp, fd2);
				found = 1;
				continue;
			}

			/* replace title */
			if (strncmp(line,"Tracktitle=", 11) == 0) {
				g_snprintf(tmp,MAXLINE,"Tracktitle=\t'%s'\n", title);
				fputs(tmp, fd2);
				found2 = 1;
				continue;
			}

			/* replace albumtitle */
			if (strncmp(line,"Albumtitle=", 11) == 0) {
				g_snprintf(tmp,MAXLINE,"Albumtitle=\t'%s'\n", cdtitle);
				fputs(tmp, fd2);
				found3 = 1;
				continue;
			}

			/* replace albumperformer */
			if (strncmp(line,"Albumperformer=", 11) == 0) {
				g_snprintf(tmp,MAXLINE,"Albumperformer=\t'%s'\n", 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", artist);
			fputs(tmp, fd2);
		}
		if (found3 == 0) {
			g_snprintf(tmp,MAXLINE,"Albumtitle=\t'%s'\n", cdtitle);
			fputs(tmp, fd2);
		}
		if (found2 == 0) {
			g_snprintf(tmp,MAXLINE,"Tracktitle=\t'%s'\n", title);
			fputs(tmp, fd2);
		}
		if (found4 == 0) {
			g_snprintf(tmp,MAXLINE,"Albumperformer=\t'%s'\n", 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];
struct stat buf;
gint ret;
time_t acttime;
gchar timestr[MAXLINE];
mstr_redirect_t *mstr;
gchar *excl;
GList *loop;

	/* first check if this file does already exist */
	if (lstat(fname, &buf) == 0) {
		g_snprintf(tmp,1024,_("File %s already exists.\nAre you sure you want to overwrite it?"), fname);
		ret = show_dialog(ICO_WARN, tmp, T_OK, T_CANCEL, NULL, 1);
		if (ret == 1) {
			return -1;
		}
	}

	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, 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 couldnt 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;
}

