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

#include "largefile.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/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
#ifdef sun 
# include <sys/audioio.h>
#endif
#ifdef aix
#include <sys/audio.h>
#endif
#ifdef hpux 
# ifndef hpux_alib
#  include <sys/audio.h>
# endif
#endif
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib.h>
#include "xcdrdata.h"
#include "xcdroast.h"
#include "main.h"

scsi_devices_t **scsidevices;
gchar **alt_scsidevices;
gint scsicount;
gint busnr;
cd_info_t cdinfo;
track_info_t **trackinfo;
gint tocstate;
gint tocnr;

writer_driver_t **drivers;
writer_driver_t **blankmodes;
writer_driver_t **drv_options;
gint drvcount;
gint drvoptcount;
gint dfrun;
gint readcdda_callback;
gint readcdda_callback2;
gint readcdda_callback3;
gint read_done;
gint read_output_ctrl;
gint read_abort_mark;
gint read_tracknr;
gchar readtrack_info_string[1024];
gfloat pct_so_far, pct_this_track;
write_track_param_t writeparams;
pid_t readcdda_pid;
gint readcd_startsector, readcd_endsector;
gint readerr_count;
gint matchnr;
gint cddb_in;
gchar cdinfo_cddb_title_bak[MAXLINE];
gint cd_is_still_the_same;
gint cdrecord_stdin, cdrecord_reload;
gchar xcdroast_version_loaded[MAXLINE];
gint delete_count, delete_start, delete_all;

static gint msinfo_nr1, msinfo_nr2;

extern gint debug;
extern gint ignorescanbus;
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 *readtrack_info_label, *readtrack_textview;
extern GtkWidget *readtrack_pbar1, *readtrack_pbar2, *readtrack_pbar3;
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;

void verify_readcd_err(gpointer pid, gint source, GdkInputCondition cond);


/* convert device-type-names back to numeric */
/* -1 means unknown */

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

void get_scsi_type_string(gchar *str, gint type,  gint removeable) {

	switch(type) {
	case 0:
		if (removeable == 0) {
			strcpy(str,text(250));
		} else {
			strcpy(str,text(251));
		}
		break;
	case 1:
		strcpy(str,text(252));
		break;
	case 2:
		strcpy(str,text(253));
		break;
	case 3:
		strcpy(str,text(254));
		break;
	case 4:
		strcpy(str,text(255));
		break;
	case 5:
		strcpy(str,text(256));
		break;
	case 6:
		strcpy(str,text(257));
		break;
	case 7:
		strcpy(str,text(258));
		break;
	case 8:
		strcpy(str,text(259));
		break;
	case 9:
		strcpy(str,text(260));
		break;
	default:
		strcpy(str,"");
		break;
	}
}


/* look in given string for "' '" and return index to it */
/* return -1 if not found */

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

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

void parse_scan(gchar *line, gint idx) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p1, *p2;
gint s_id;
gchar s_vendor[9];
gchar s_model[17];
gchar s_rev[5];
gint s_removable;
gint s_type;
gint next;

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

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

	/* a line with an error message? */
	/* 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) {
		strip_string(line); 

		/* get scsi-id */
		p1=strtok(line,")");
		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,"");
		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 = 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]->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);
		}
	}
}


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

void parse_inq(gchar *line, gint idx) {
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]->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);
	}
}


/* print memory-structure with scsidevices (debug purposes) */

void print_scsidevices() {
gint count;

	dodebug(2,"------ cdrecord scsidevices-structure -----\n");
	count = 0;
	while(scsidevices[count] != NULL) {
		dodebug(2,"%d:%d %s %s %s %d,%d (alt: %d) (ssize: %d)\n",
			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++;
	}
}


/* does scan for a single device only */

void scanbus_single(gchar *dev, gint idx) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
FILE *fpin;

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

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

}


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

void scanbus_via_inq(gchar *dev, gint idx) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
FILE *fpin;

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

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


/* call cdrecord -scanbus */
/* does rely very strict on the output-format... does use fixed length
   deleminitors to be sure that quotes within an id-string will handled
   fine - therefore no strtok usage */
 
void scanbus() {
gchar line[MAXLINE];
FILE *fpin;
gint stat,i;

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

	/* check if we have to do full scanbus, or only partial */
	if (alt_scsidevices[0] != NULL) {
		i = 0;
		while (alt_scsidevices[i] != NULL) {
			scanbus_single(alt_scsidevices[i],i);
			i++;
		}		
		if (debug) print_scsidevices();
		/* skip automatic part and return */
		return;
	}

#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],i);
		i++;
	}		
	if (debug) print_scsidevices();

	/* skip automatic part and return */
	return;
#endif

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

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

	if (debug) print_scsidevices();

	/* no devices found? */
	if (scsicount == 0 && ignorescanbus == 0) {
		/*
		g_warning("Failed to scan the SCSI-bus. Either no permission to access the\ngeneric scsi devices or no SCSI support in the kernel.\n(For ATAPI (IDE) devices you need to install SCSI-Emulation first)\n(You can start X-CD-Roast anyway with the -i option)\n");
		*/

   		/* we need a toplevel for the show_dialog function */
                toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
                gtk_widget_realize(toplevel);
                stat = show_dialog(ICO_WARN, text(414), text(7), text(199), NULL, 0);
		gtk_widget_destroy(toplevel);

		/* exit clicked */
		if (stat == 0) 
			gtk_exit(1);
	}
}


/* interpret output of driver=help and sort into memory structure */

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

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

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 -jcharset 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," -jcharset 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 joliet 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();
}


/* print memory-structure with driver-options (debug purposes) */

void print_drv_options() {
gint count;

	dodebug(2,"------ cdrecord drivers-options-structure -----\n");
	if (drv_options == NULL) return;

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



/* allocate memory and sort data into driver-opts-structure */

void add_drv_option(gchar *opt, gchar *desc) {

	strip_string(opt);
	strip_string(desc);

	/* no option found? */
	if (strcmp(opt,"None") == 0) 
		return;

	/* first option? allocate memory */
	if (drvoptcount == 0) {
		drv_options = g_new0(writer_driver_t *,MAXDRIVERS);
	}		

	drv_options[drvoptcount] = g_new(writer_driver_t,1);
	drv_options[drvoptcount]->driver = g_strdup(opt);
	drv_options[drvoptcount]->desc = g_strdup(desc);

	drvoptcount++;
	if (drvoptcount >= MAXDRIVERS) {
		g_error("Error: More than %d writer driver-options found\n",MAXDRIVERS);
	}
}


/* call cdrecord driveropts=help */

void scanoptions(gint write_devnr) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar line[MAXLINE];
gint match,count;
FILE *fpin;
gchar *p;

	if (write_devnr == -1) return;

	/* free structure first */
	if (drv_options != NULL) {
		count = 0;
		while (drv_options[count] != NULL) {
			g_free(drv_options[count]->driver);
			g_free(drv_options[count]->desc);
			g_free(drv_options[count]);
			count++;
		}
		g_free(drv_options);
		drv_options = NULL;
		drvoptcount = 0;
	}


        /* get bus,id,lun string */
        if (convert_devnr2busid(write_devnr,tmp) != 0) {
		/* ignore error here */
		return;
        }

	get_wrap_path("CDRECORD",tmp2);
	g_snprintf(line, MAXLINE, 
		"%s dev= \"%s\" driveropts=help -checkdrive 2>&1 >/dev/null",
		tmp2, tmp);

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

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

		if (strncmp(line,"Driver options:", 15) == 0) {
			match = 1;
			continue;
		}

		if (match) {
			p = strtok(line," \t");
			if (p) {
				strcpy(tmp,p);
				p = strtok(NULL,"");
				if (p) {
					strcpy(tmp2,p);
					add_drv_option(tmp,tmp2);
				}	
			}
		}
        }

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

	if (debug) print_drv_options();
}


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

	/* skip first line */
	if (strncmp(line,"Filesystem",10) != 0) {
	        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%   /

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)
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
#ifdef sun
	/* 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

	/* 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
#ifdef sun

	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;

	/* 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 V%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);
	strcpy(tmp2,setupdata.writer_vendor);
	g_snprintf(tmp,MAXLINE,"WRITER_VENDOR = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	strcpy(tmp2,setupdata.writer_model);
	g_snprintf(tmp,MAXLINE,"WRITER_MODEL = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITER_MODE = %d\n",setupdata.writer_mode);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITER_SPEED = %d\n",setupdata.writer_speed);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"WRITER_FIFO = %d\n",setupdata.writer_fifo);
	fputs(tmp,fd);

	g_snprintf(tmp,MAXLINE,"READDEV1_DEVNR = %d\n",setupdata.readdev1_devnr);
	fputs(tmp,fd);
	strcpy(tmp2,setupdata.readdev1_vendor);
	g_snprintf(tmp,MAXLINE,"READDEV1_VENDOR = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	strcpy(tmp2,setupdata.readdev1_model);
	g_snprintf(tmp,MAXLINE,"READDEV1_MODEL = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"READDEV2_DEVNR = %d\n",setupdata.readdev2_devnr);
	fputs(tmp,fd);
	strcpy(tmp2,setupdata.readdev2_vendor);
	g_snprintf(tmp,MAXLINE,"READDEV2_VENDOR = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	strcpy(tmp2,setupdata.readdev2_model);
	g_snprintf(tmp,MAXLINE,"READDEV2_MODEL = \"%s\"\n",convert_escape(tmp2));
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"AUDIOREAD_INTERFACE = %d\n",setupdata.audioread_interface);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"AUDIOREAD_SPEED = %d\n",setupdata.audioread_speed);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"AUDIOREAD_OVERLAP = %d\n",setupdata.audioread_overlap);
	fputs(tmp,fd);
	g_snprintf(tmp,MAXLINE,"AUDIOREAD_SECTORBURST = %d\n",setupdata.audioread_sectorburst);
	fputs(tmp,fd);

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

	g_snprintf(tmp,MAXLINE,"LANGUAGE = %d\n",setupdata.language);
	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);
	g_snprintf(tmp,MAXLINE,"OPTION_DEFWRITEMODE = %d\n",setupdata.def_write_mode);
	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 
   allowed */

gint load_setup_config(gchar *fname, gint rootconf) {
FILE *fd;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];
GList *tmp_image_dirs;

	tmp_image_dirs = NULL;

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

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

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

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

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

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

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

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

		if (rootconf || setupdata.root_option_change_writer) {
			if (strcmp("WRITER_DEVNR",id) == 0) {
				setupdata.writer_devnr = atoi(value);
			}
			if (strcmp("WRITER_VENDOR",id) == 0) {
				strncpy(setupdata.writer_vendor,value,9);
				setupdata.writer_vendor[8] = '\0';
			}
			if (strcmp("WRITER_MODEL",id) == 0) {
				strncpy(setupdata.writer_model,value,17);
				setupdata.writer_model[16] = '\0';
			}
			if (strcmp("WRITER_MODE",id) == 0) {
				setupdata.writer_mode = atoi(value);
			}
		}	
		if (rootconf || setupdata.root_option_change_writeparam) {
			if (strcmp("WRITER_SPEED",id) == 0) {
				setupdata.writer_speed = atoi(value);
			}
			if (strcmp("WRITER_FIFO",id) == 0) {
				setupdata.writer_fifo = atoi(value);
			}
		}
		if (rootconf || setupdata.root_option_change_reader) {
			if (strcmp("READDEV1_DEVNR",id) == 0) {
				setupdata.readdev1_devnr = atoi(value);
			}
			if (strcmp("READDEV1_VENDOR",id) == 0) {
				strncpy(setupdata.readdev1_vendor,value,9);
				setupdata.readdev1_vendor[8] = '\0';
			}
			if (strcmp("READDEV1_MODEL",id) == 0) {
				strncpy(setupdata.readdev1_model,value,17);
				setupdata.readdev1_model[16] = '\0';
			}
			if (strcmp("READDEV2_DEVNR",id) == 0) {
				setupdata.readdev2_devnr = atoi(value);
			}
			if (strcmp("READDEV2_VENDOR",id) == 0) {
				strncpy(setupdata.readdev2_vendor,value,9);
				setupdata.readdev2_vendor[8] = '\0';
			}
			if (strcmp("READDEV2_MODEL",id) == 0) {
				strncpy(setupdata.readdev2_model,value,17);
				setupdata.readdev2_model[16] = '\0';
			}
		}
		if (rootconf || setupdata.root_option_change_readparam) {
			if (strcmp("AUDIOREAD_INTERFACE",id) == 0) {
				setupdata.audioread_interface = atoi(value);
			}
			if (strcmp("AUDIOREAD_SPEED",id) == 0) {
				setupdata.audioread_speed = atoi(value);
			}
			if (strcmp("AUDIOREAD_OVERLAP",id) == 0) {
				setupdata.audioread_overlap = atoi(value);
			}
			if (strcmp("AUDIOREAD_SECTORBURST",id) == 0) {
				setupdata.audioread_sectorburst = 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 (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) {
			setupdata.language = atoi(value);
		}

		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 (rootconf || setupdata.root_option_change_writeparam) {
			if (strcmp("OPTION_DEFWRITEMODE",id) == 0) {
				setupdata.def_write_mode = atoi(value);
				curset.writemode = setupdata.def_write_mode;
			}
		}

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

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,"");
	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,"");
		strcpy(tmp2,p1);
		strip_string(tmp2);

		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 {
			ret = decode_title_artist(tmp2,tmp,tmp3);
			if (ret != 0) {
				g_error("got unexpected artist/title\n");
			}
			trackinfo[tocnr]->title = g_strdup(tmp);
			trackinfo[tocnr]->artist = g_strdup(tmp3);
		}
	}	

	
	tocnr++;
}


/* print memory-structure with toc-data (debug purposes) */

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

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[1] != 'h') {
			parse_toc_line(line);
		}	

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

	return 0;
}


/* call cdda2wav -J to get the cd-toc */

void get_cd_toc(gint devnr) {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
FILE *fpin;
gint ret;
gchar old_cdtext_title[MAXLINE];
gchar old_cdtext_artist[MAXLINE];
gint mmaperror;


	/* check if cd is loaded */
	if (!check_medium_loaded(devnr)) {
		/* not loaded */
		cdinfo.nr_tracks = -1;
		return;
	}

	cdinfo.have_cdtext = -1;
	cdinfo.have_cdextra = -1;
	cdinfo.nr_tracks = -1;
	
	tocstate = 0;
	tocnr = 0;
	cd_is_still_the_same = 0;
	mmaperror = 0;

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

	/* 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 -v1 2>&1",
		tmp2,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,"readtoc: %s", line);
		mmaperror += parse_toc(line);
        }

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

	/* 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 (old_cdtext_title[0] != '\0') {
			g_free(cdinfo.title);
			cdinfo.title = g_strdup(old_cdtext_title);
		}
		if (old_cdtext_artist[0] != '\0') {
			g_free(cdinfo.artist);
			cdinfo.artist = g_strdup(old_cdtext_artist);
		}
	}

	/* have we hit the mmap error? */
	if (mmaperror > 0) {
                ret = show_dialog(ICO_WARN, text(520), T_ANYWAY, text(7), NULL, 1);
                if (ret == 1) 
                        gtk_exit(1);
	}
	
	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 ,NULL) < 0) {
			g_error("execl error\n");
		}
 
	}

	return(pid);
}


/* rebuild a cmdline-string into a list of arguments..handle quotes and
   escaped chars nicely */

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 */
	strcpy(callpath,arglist[0]);
	strcpy(tmp,arglist[0]);
	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++;
		}
	}
}


/* start subprocess and reroute stdin, stderr and stdout */
/* does NOT use the shell as spawner */

pid_t full_dpl_pipe3(gint *out, gint *in, gint *err, gchar *cmd) { 
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 (pipe(fd1) <0 || pipe(fd2) <0 || pipe(fd3) <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 */

void full_dpl_pipe4(pid_t *pidnr1, pid_t *pidnr2, gint *in1, gint *err1, 
		    gchar *cmd, gint *out2, gint *err2, gchar *cmd2) {
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 (pipe(fd1) <0 || pipe(fd2) <0 || pipe(fd3) <0 || pipe(fd4) <0
	   || pipe(pipefd) <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 stdout\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
#ifdef sun
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
#ifdef sun

	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
#ifdef sun
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 
#ifdef sun

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

	if (readcdda_pid != -1) {

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


/* in bulk mode update the info-label of read tracks */

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,text(157), read_tracknr,
			writeparams.nrtracks, tmp);
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), tmp2);
}


/* parse output of cdda2wav and update sliders */

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,text(317),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");
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			NULL,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),text(318));	
	}	

	/* 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,text(319),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);

		/* 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) {
			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");
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			NULL,NULL,NULL, line, strlen(line));
	}	
}


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

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");
	gtk_text_insert(GTK_TEXT(readtrack_textview),
			NULL,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];
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;
	}

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

	/* set info-label */
	convert_kbytes2mbminstring(kbyte, tmp);
	g_snprintf(tmp2,MAXLINE,text(157), viewtrack, nrtracks, tmp);
	gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp2);	
	strcpy(readtrack_info_string, tmp2);

	g_snprintf(tmp2,MAXLINE,text(163), 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 (curset.audioread_speed > 0) {
		g_snprintf(tmp3,MAXLINE,"-S %d", curset.audioread_speed); 
	} else {
		strcpy(tmp3,"");
	}

	/* have to scan for indexes? */
	if (curset.indexscan != 0) {
		strcpy(tmp4,"-i 1");
	} else {
		strcpy(tmp4,"");
	}

	get_wrap_path("CDDA2WAV",tmp5);
	g_snprintf(cmd,MAXLINE,
		   "%s -D \"%s\" -g -v2 -O wav -t %s %s %s -P %d -n %d \"%s\"",
		   tmp5,tmp,tmp2,tmp3,tmp4,
		   setupdata.audioread_overlap,setupdata.audioread_sectorburst,
		   convert_escape(fname));

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

	/* 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),
		NULL,NULL,NULL, "\n", 1);

	/* error while reading? */
	if (read_done != 0 && read_abort_mark == 0) {
		g_snprintf(tmp,MAXLINE,text(159), starttrack, nrtracks);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);	
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(164));	
		return 1;
	}
	/* aborted? */
	if (read_abort_mark == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),text(247));	
		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 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;
        read_abort_mark = 0;

	/* init track-label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(320));
	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 (curset.audioread_speed > 0) {
                g_snprintf(tmp3,MAXLINE,"-S %d", curset.audioread_speed); 
        } else {
                strcpy(tmp3,"");
        }

        /* have to scan for indexes? */
        if (curset.indexscan != 0) {
                strcpy(tmp4,"-v30");
        } else {
                strcpy(tmp4,"-v2");
        }

	get_wrap_path("CDDA2WAV",tmp5);
        g_snprintf(cmd,MAXLINE,
                   "%s -D \"%s\" -g -O wav %s %s -P %d -n %d -B",
                   tmp5,tmp,tmp3,tmp4,
                   setupdata.audioread_overlap,setupdata.audioread_sectorburst
                   );

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

        /* 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),
                NULL,NULL,NULL, "\n", 1);

        /* error while reading? */
        if (read_done != 0 && read_abort_mark == 0) {
                g_snprintf(tmp,MAXLINE,text(159), read_tracknr, 
			writeparams.nrtracks);
                gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);        
                gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(164));  
                return 1;
        }
        /* aborted? */
        if (read_abort_mark == 1) {
                gtk_label_set_text(GTK_LABEL(readtrack_info_label),text(247));  
                return 1;
        }

	return 0;
}


/* parse output of readcd and update sliders */

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

		/* 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),text(416));	
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(417));	
	}

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

	/* forward most other output to textview-window */
	strcat(line,"\n");
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		NULL,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;
	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,text(158), viewtrack, nrtracks, tmp);
	gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp2);	

	g_snprintf(tmp2,MAXLINE,text(163), 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;

        /* now check which sectorsize we got */
        sectsize = get_sectorsize(devnr);
        startoffset *= DATASECTORSIZE / sectsize;
        endoffset *= DATASECTORSIZE / sectsize;

	get_wrap_path("READCD",tmp3);
	strcpy(tmptmp,fname);
	g_snprintf(cmd,MAXLINE,
		   "%s dev= \"%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); 

	/* 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),
		NULL,NULL,NULL, "\n", 1);

	/* error while reading? */
	if ((read_done != 0 && read_abort_mark == 0) || read_output_ctrl == 2) {
		g_snprintf(tmp,MAXLINE,text(160), starttrack, nrtracks);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);	
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(164));	
		return 1;
	}
	/* aborted? */
	if (read_abort_mark == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),text(247));	
		return 1;
	}

	return 0;
}


/* checks if a disk is loaded in drive 
   return 0 if not, 1 if loaded and ready */
 
gint check_medium_loaded(gint devnr) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar line[MAXLINE];
gchar *p;
FILE *fpin;
gint ret, found, sectsize;

	found = 0;

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

	/* build command line */
	get_wrap_path("READCD",tmp2);
	g_snprintf(line,MAXLINE,"%s dev= \"%s\" f=- sectors=0-0 2>&1",
		tmp2,tmp);

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

	/* read all you can get and throw away */
	for (;;) {	
		if (fgets(line,MAXLINE,fpin) == NULL)
			break;
		dodebug(10,"check_medium_loaded: %s", line);

		/* got the message that no medium is present?
		   in this case, ignore return code */ 
		if (strstr(line,"medium not present")) {
			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);
                                set_sectorsize(devnr,sectsize);
                        }
                }
	}
	
	/* we are only interested in the exit-status */
	ret = pclose(fpin);
	if (ret == -1) {
		g_error("pclose error\n");
	}

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

	if (ret == 0 && 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);
	/* 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 [%lld 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 [%lld 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);

		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,"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("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("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);
	p = rindex(tmp,'.');
	if (!p) return 1;
	*p = '\0';
	strcpy(tmp2,tmp);
	strcat(tmp2,XCDROAST_INFO_EXT);

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

	/* 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) {
		strcpy(tmp2,cdinfo.cddb_dtitle);
		g_snprintf(tmp,MAXLINE,"cd_cddb_dtitle = \"%s\"\n",
			convert_escape(tmp2));
		fputs(tmp,fd);
	}
	g_snprintf(tmp,MAXLINE,"cd_discid = \"%s\"\n",
		convert_escape(cdinfo.cddb_discid));
	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);
	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(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* build command line */
	get_wrap_path("CDRECORD",tmp3);
	g_snprintf(line,MAXLINE,"%s dev= \"%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; 
				}
			}
		}	
		gtk_text_insert(GTK_TEXT(txt), 
			NULL,NULL,NULL, line, strlen(line));
        }

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


/* call cdrecord -prcap to find out if there is a mmap error */
/* if so, then the cdrecord binary is broken..warn about it */
/* return 1 when error was found */

gint check_for_mmap_error() {
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar tmp3[MAXLINE];
FILE *fpin;
gint errcount;
gint i, devnr;

	/* get a devicenr of the first found device (it does not 
	   matter which one we check..we just have to force cdrecord
	   to access a device */

	devnr = -1;
	i = 0;
	while(scsidevices[i] != NULL) {
		if (scsidevices[i]->type == 4 || scsidevices[i]->type == 5 ) {
			devnr = scsidevices[i]->devnr;
			break;
		}
		i++;
	}

	if (devnr == -1) {
		/* nothing found? well, give up */
		return 0;
	}

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

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

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


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

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

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

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

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


/* 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(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* build command line */
	get_wrap_path("CDRECORD",tmp3);
	g_snprintf(line,MAXLINE,"%s dev= \"%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(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* build command line */
	get_wrap_path("CDRECORD",tmp3);
	g_snprintf(line,MAXLINE,"%s dev= \"%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");
        }
}


/* 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(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* build command line */
	get_wrap_path("CDRECORD",tmp3);
	g_snprintf(line,MAXLINE,"%s dev= \"%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");
        }

	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), 
				NULL,NULL,NULL, line, strlen(line));
		}
        }

	/* no atip-information available? */
	if (found_atip == 0) {
		/* generate error-message */
		strncpy(line,text(177),MAXLINE);
		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 */

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

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

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(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* build command line */
	get_wrap_path("CDRECORD",tmp3);
	g_snprintf(line,MAXLINE,"%s dev= \"%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);
        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 == 1) {
                        cdrecord_reload = 0;
                        show_dialog(ICO_WARN, text(277), 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);

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

	*nr1 = msinfo_nr1;
	*nr2 = msinfo_nr2; 
}


/* interpret output of blank=help and sort into memory structure */

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

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, sectsize;

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

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

        /* now check which sectorsize we got */
        sectsize = get_sectorsize(devnr);
        s1 *= DATASECTORSIZE / sectsize;
        s2 *= DATASECTORSIZE / sectsize;

	/* build command-line */
	get_wrap_path("READCD",tmp2);
	g_snprintf(cmd,MAXLINE,"%s dev= \"%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 */

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

		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) {
		strcpy(tmp, line+(strlen(line)-8));
		if (strcmp(tmp, "seconds.") == 0) {
			return;
		}
	}

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

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

}


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

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

	gtk_text_insert(GTK_TEXT(readtrack_textview),
		NULL,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 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(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.writer_speed >= 0) {
                g_snprintf(tmp4,MAXLINE,"speed=%d ", curset.writer_speed); 
                strcat(tmp3, tmp4);
        }
	
	get_wrap_path("CDRECORD", tmp5);
	g_snprintf(cmd, MAXLINE,
		"%s dev= \"%s\" -v %sblank=%s", 
		tmp5, tmp, 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);

	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 == 1) {
                        cdrecord_reload = 0;
                        show_dialog(ICO_WARN, text(277), 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, text(194),
				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);

	/* no disk */
	if (read_output_ctrl == 2) {
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			NULL,NULL,NULL,"\n",1);	
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			NULL,NULL,NULL,text(190), strlen(text(190)));
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			NULL,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; 
}


/* get output of cdrecord */

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;
gfloat pval, pval2, pval3;

	n = read_line(source, line, MAXLINE);

	/* finished? */
	if (n <= 0) {
		dodebug(10,"cdrecord (stdout): EOF\n", line);
		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 (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) {
		strcpy(tmp, line+(strlen(line)-8));
		if (strcmp(tmp, "seconds.") == 0) {
			/* mark we are now ready to rumble */
			read_output_ctrl = 3;
			return;
		}
	}

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

		/* parser */
		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;
				if (writeparams.tracktype[tnr] == 0) {
					/* data track */
					convert_frames2mbstring(
						writeparams.frames[tnr],tmp);
					g_snprintf(tmp2,MAXLINE, text(207),
						tnr, 
						writeparams.nrtracks, tmp);
					if (writeparams.simulation) 
						strcat(tmp2,text(216));
					gtk_label_set_text(
						GTK_LABEL(readtrack_info_label), 
						tmp2);
				} else {
					/* audio track */
					convert_frames2minstring(
						writeparams.frames[tnr],tmp);
					g_snprintf(tmp2,MAXLINE, text(206),
						tnr, 
						writeparams.nrtracks, tmp);
					if (writeparams.simulation) 
						strcat(tmp2,text(216));
					gtk_label_set_text(
						GTK_LABEL(readtrack_info_label), 
						tmp2);
				}
				/* small view label */ 
				g_snprintf(tmp2,MAXLINE, text(210),
					tnr, writeparams.nrtracks);
				gtk_label_set_text(
					GTK_LABEL(readtrack_small_info),tmp2);
			} else {
				/* extract fifo value */
				strcpy(tmp,p);
				p = rindex(tmp,' ');
				if (p == NULL) return;
				fifoval = atoi(p);
			}

			/* 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]));
			if (pval2 > 1.0) pval2 = 1.0;

			if (writeparams.nrtracks != 1) {
				gtk_progress_set_percentage(
					GTK_PROGRESS(readtrack_pbar2), pval2);
			} else {
				/* just one track? total is the same as track */
				gtk_progress_set_percentage(
					GTK_PROGRESS(readtrack_pbar2), pval);
			}	

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

			/* 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), text(213));
	}

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

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

}

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", line);
		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 && curset.writemode == 0) {
				read_output_ctrl = 4;
			} 
		}
	}	


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

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


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

	/* 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;
	
	strcpy(outcmd,"");

	/* set info label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(215));

	/* 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(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}
	
	/* the writer does support burnproof? */
	if (does_support_burnproof()) {
		if (curset.writeburnfree) {
			strcpy(tmp3,"driveropts=burnfree");
		} else {
			strcpy(tmp3,"driveropts=noburnfree");
		}
	} else {
		strcpy(tmp3,"");
	}

	/* build command line */
	get_wrap_path("CDRECORD", tmp2);
	g_snprintf(cmd, MAXLINE*10,
		"%s dev= \"%s\" fs=%dk %s -v -useinfo", 
		tmp2, tmp, setupdata.writer_fifo, tmp3);

	if (setupdata.writer_mode >= 0) {
		if (drivers[setupdata.writer_mode] != NULL) {
			g_snprintf(tmp,MAXLINE," driver=%s", 
				drivers[setupdata.writer_mode]->driver);
			strcat(cmd,tmp);
		}
	}
	
	if (curset.writer_speed >= 0) {
		g_snprintf(tmp,MAXLINE," speed=%d", curset.writer_speed);	
		strcat(cmd, tmp);
	}

	if (curset.multisession == 0) {
		if (curset.writemode == 0) {
			strcat(cmd," -dao");
		}
		if (curset.writemode == 2) {
			strcat(cmd," defpregap=0");
		}
		if (curset.writemode == 3) {
			strcat(cmd," -raw96r");
		}
		if (curset.writemode == 4) {
			strcat(cmd," -raw96p");
		}
		if (curset.writemode == 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.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),
		NULL,NULL,NULL,outcmd, strlen(outcmd));

	/* yes...command line is done */
	readcdda_pid = full_dpl_pipe3(&read_out,&cdrecord_stdin,&read_err,cmd);

	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, NULL);
	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 == 1) {
			cdrecord_reload = 0;
			show_dialog(ICO_WARN, text(277), 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, text(194),
				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), text(217));
		return 1;
	}
	/* cannot send cue sheet */
	if (read_output_ctrl == 4) {
		show_dialog(ICO_WARN,text(289),T_OK, NULL, NULL, 0);
	}

	/* aborted write ? */
	if (read_output_ctrl == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(218));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(211));
		return 1;
	}

	/* error while reading ?*/
	if (read_done != 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(208));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(211));
		return 1;
	} else {
		/* all ok */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(209));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(212));
		return 0;
	}
}


/* call cdrecord to write a cd on the fly */
/* works currently only for cds with a single data-track */

gint start_write_onthefly_action(gint read_devnr, gint write_devnr) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar tmp3[MAXLINE];
gchar tmp4[MAXLINE];
gchar cmd[MAXLINE*10];  /* extra big buffer for very long cdrecord options */
gchar cmd2[MAXLINE];
gchar outcmd[MAXLINE];
gint read_out, read_err, read_err2;
gint ret, tracknr;
pid_t pid1, pid2;
gint endsector, sectsize;

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

	strcpy(outcmd,"");

	/* set info label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(215));

	/* 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 */
	/* 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);
        endsector *= DATASECTORSIZE / sectsize;

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

	get_wrap_path("READCD", tmp2);
	g_snprintf(cmd,MAXLINE,
		"%s dev= \"%s\" sectors=0-%d -s f=-",
		tmp2, tmp, endsector);

	/* build command line of write process */

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

	/* the writer does support burnproof? just enable it for now */
	if (does_support_burnproof()) {
		if (curset.writeburnfree) {
			strcpy(tmp4,"driveropts=burnfree");
		} else {
			strcpy(tmp4,"driveropts=noburnfree");
		}
	} else {
		strcpy(tmp4,"");
	}

	get_wrap_path("CDRECORD", tmp3);
	g_snprintf(cmd2, MAXLINE*10,
		"%s dev= \"%s\" fs=%dk %s -v -useinfo",  
		tmp3, tmp, setupdata.writer_fifo, tmp4);

	if (setupdata.writer_mode >= 0) {
		if (drivers[setupdata.writer_mode] != NULL) {
			g_snprintf(tmp,MAXLINE," driver=%s", 
				drivers[setupdata.writer_mode]->driver);
			strcat(cmd2,tmp);
		}
	}
	
	if (curset.writer_speed >= 0) {
		g_snprintf(tmp,MAXLINE," speed=%d", curset.writer_speed);	
		strcat(cmd2, tmp);
	}
	if (curset.multisession == 0) {
		if (curset.writemode == 0) {
			strcat(cmd2," -dao");
		}
		if (curset.writemode == 2) {
			strcat(cmd2," defpregap=0");
		}
		if (curset.writemode == 3) {
			strcat(cmd2," -raw96r");
		}
		if (curset.writemode == 4) {
			strcat(cmd2," -raw96p");
		}
		if (curset.writemode == 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.writecdtext == 1) {
		strcat(cmd2," -text");
	}

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

	/* save partial 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] = endsector;
	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,"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),
		NULL,NULL,NULL,outcmd, strlen(outcmd));

	/* yes...command line is done */
	full_dpl_pipe4(&pid1,&pid2,NULL,&read_err,cmd,&read_out,&read_err2,cmd2);
	readcdda_pid = pid2;

	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, NULL);
	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 == 1) {
			cdrecord_reload = 0;
			show_dialog(ICO_WARN, text(277), T_OK, NULL, NULL, -1);
			
			/* now send CR to cdrecord to let it continue */
/*
			THIS IS A BUG IN CDRECORD BECAUSE NO WAY TO SEND
			THE CR WHEN IN PIPE MODE (because stdin is used)
			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, text(194),
				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), text(217));
		return 1;
	}

	/* cannot send cue sheet */
	if (read_output_ctrl == 4) {
		show_dialog(ICO_WARN,text(289),T_OK, NULL, NULL, 0);
	}

	/* aborted write ? */
	if (read_output_ctrl == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(218));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(211));
		return 1;
	}

	/* error while reading ?*/
	if (read_done != 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(208));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(211));
		return 1;
	} else {
		/* all ok */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(209));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(212));
		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;

	/* 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;
	
	/* set info label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(215));

	/* get bus,id,lun string */
	if (convert_devnr2busid(devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}
		
	/* build command line */
	get_wrap_path("CDRECORD", tmp2);
	g_snprintf(cmd, MAXLINE*10,
		"%s dev= \"%s\" -v -fix", tmp2, tmp);

	if (setupdata.writer_mode >= 0) {
		if (drivers[setupdata.writer_mode] != NULL) {
			g_snprintf(tmp,MAXLINE," driver=%s", 
				drivers[setupdata.writer_mode]->driver);
			strcat(cmd,tmp);
		}
	}
	
	if (curset.writer_speed >= 0) {
		g_snprintf(tmp,MAXLINE," speed=%d", curset.writer_speed);	
		strcat(cmd, tmp);
	}
	if (curset.multisession == 0) {
		if (curset.writemode == 0) {
			strcat(cmd," -dao");
		}
		if (curset.writemode == 2) {
			strcat(cmd," defpregap=0");
		}
		if (curset.writemode == 3) {
			strcat(cmd," -raw96r");
		}
		if (curset.writemode == 4) {
			strcat(cmd," -raw96p");
		}
		if (curset.writemode == 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");
	}

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

	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, NULL);
	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 == 1) {
			cdrecord_reload = 0;
			show_dialog(ICO_WARN, text(277), 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, text(194),
				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), text(217));
		return 1;
	}
	/* cannot send cue sheet */
	if (read_output_ctrl == 4) {
		show_dialog(ICO_WARN,text(289),T_OK, NULL, NULL, 0);
	}

	/* aborted write ? */
	if (read_output_ctrl == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(218));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(211));
		return 1;
	}

	/* error while reading ?*/
	if (read_done != 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(208));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(211));
		return 1;
	} else {
		/* all ok */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(209));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(212));
		return 0;
	}
}


/* parse output of rmtool and update sliders and textbox */

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

}


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

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);
	
	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), text(227));

	/* 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);
		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), text(228));
	} else 
	if (read_done == 2) {
		/* delete canceled */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(229));
	} else {
		/* some error */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(230));
	}

	return 0;
}


/* parse output of verfify-tool and update sliders */

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

		/* 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");
	gtk_text_insert(GTK_TEXT(readtrack_textview),
		NULL,NULL,NULL, line, strlen(line));
}


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

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)pid, &ret, WNOHANG);

		return;
	}

	dodebug(10,"vry-readcd (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;
	read_output_ctrl = 0;
	read_abort_mark = 0;

	/* set info-label */
	convert_kbytes2mbminstring(kbyte, tmp);
	g_snprintf(tmp2,MAXLINE,text(240), viewtrack, nrtracks, tmp);
	gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp2);	

	g_snprintf(tmp2,MAXLINE,text(244), 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;

        /* now check which sectorsize we got */
        sectsize = get_sectorsize(devnr);
        startoffset *= DATASECTORSIZE / sectsize;
        endoffset *= DATASECTORSIZE / sectsize;

	get_wrap_path("READCD", tmp3);
	g_snprintf(cmd,MAXLINE,
		   "%s dev= \"%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);
	readcdda_pid = pid1;

	/* 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),
		NULL,NULL,NULL, "\n", 1);

	/* error while reading? */
	if (read_done != 0 && read_abort_mark == 0) {
		g_snprintf(tmp,MAXLINE,text(242), starttrack, nrtracks);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);	
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(245));	
		return 1;
	}
	/* compare aborted? */
	if (read_abort_mark != 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(246));	
		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];
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;
	read_output_ctrl = 0;
	read_abort_mark = 0;

	/* set info-label */
	convert_kbytes2mbminstring(kbyte, tmp);
	g_snprintf(tmp2,MAXLINE,text(239), viewtrack, nrtracks, tmp);
	gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp2);	

	g_snprintf(tmp2,MAXLINE,text(244), 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 (curset.audioread_speed > 0) {
		g_snprintf(tmp3,MAXLINE,"-S %d", curset.audioread_speed); 
	} else {
		strcpy(tmp3,"");
	}

	get_wrap_path("CDDA2WAV", tmp4);
	g_snprintf(cmd,MAXLINE,
		  "%s -D \"%s\" -g -H -v2 -O wav -t %s %s -P %d -n %d -",
		  tmp4,tmp,tmp2,tmp3,
		  setupdata.audioread_overlap,setupdata.audioread_sectorburst);
	
	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);
	readcdda_pid = pid1;

	/* 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),
		NULL,NULL,NULL, "\n", 1);

	/* error while reading? */
	if (read_done != 0 && read_abort_mark == 0) {
		g_snprintf(tmp,MAXLINE,text(241), starttrack, nrtracks);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp);	
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(245));	
		return 1;
	}
	/* compare aborted? */
	if (read_abort_mark != 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(246));	
		return 2;
	}

	return 0;
}


/* parse output of cddbtool and update textbox */

void read_cddbtool_out(gpointer data, gint source, GdkInputCondition cond) {
gint n;
gint ret;
gint code;
gchar line[MAXLINE];
gchar tmp[MAXLINE];
gchar *p;
gchar *rowdata[1];

	/* 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] = 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,text(111), 
			setupdata.cddb_host, setupdata.cddb_port);
		break;
	case 1: 
		strcpy(tmp,text(117));
		break;
	case 2: 
		strcpy(tmp,text(119));
		break;
	case 3: 
		strcpy(tmp,text(121));
		break;
	case 4: 
		/* close matches */
		if (matchnr == 1) {
			strcpy(tmp,text(263));
		} else {
			g_snprintf(tmp,MAXLINE,text(122), matchnr);
		}
		break;
	case 5: 
		/* exact matches */
		if (matchnr == 1) {
			strcpy(tmp,text(264));
		} else {
			g_snprintf(tmp,MAXLINE,text(123), matchnr);
		}
		break;
	case 6: 
		strcpy(tmp,text(124));
		break;
	case 7:
		/* got disc-title */
		strcpy(tmp,line);
		if (extract_quoted(tmp) != 0) 
			return;

		/* we want title first, then artist */
		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);

		/* recycle matchnr-var as track-counter */
		matchnr = 0;

		return;
	case 8:
		/* got track-title */
		strcpy(tmp,line);
		if (extract_quoted(tmp) != 0) 
			return;

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

		matchnr++;
		return;
		
	/* Error-codes */
	case -1:
		strcpy(tmp,text(112));
		break;
	case -2:
		strcpy(tmp,text(113));
		break;
	case -3:
		strcpy(tmp,text(114));
		break;
	case -4:
		strcpy(tmp,text(115));
		break;
	case -5:
		strcpy(tmp,text(116));
		break;
	case -6:
		strcpy(tmp,text(118));
		break;
	case -7:
		strcpy(tmp,text(120));
		break;
	case -8:
		strcpy(tmp,text(125));
		break;
	case -9:
		strcpy(tmp,text(126));
		break;
	case -10:
		strcpy(tmp,text(261));
		break;
	case -11:
		strcpy(tmp,text(262));
		break;
	}

	gtk_label_set_text(GTK_LABEL(cddb_info_label), tmp);
}


/* do lookup the cddb-database */

gint start_cddb_lookup_action() {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
char cmd[MAXLINE];
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 */
	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);
	g_snprintf(cmd,MAXLINE,"%s/%s -s %s -p %d -u \"%s\" -h %s -q \"%s\"", 
		sharedir, CDDBTOOL, setupdata.cddb_host, setupdata.cddb_port,
		username, hostname, tmp);
	
	dodebug(1, "spawning: %s\n",cmd);
	dolog(2, "CDDB lookup for discid %s\n", cdinfo.cddb_discid);
	dolog(3, "Executing: %s\n", cmd);
	
	readcdda_pid = full_dpl_pipe3(&read_out,&cddb_in,NULL,cmd);

	fcntl(read_out, F_SETFL, O_NONBLOCK);

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


	/* 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 V%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<16; i++) {
		g_snprintf(tmp,MAXLINE,"OPT_%d = %d\n", i, 
			masterparam.opt[i]);
        	fputs(tmp,fd);
	}

        g_snprintf(tmp,MAXLINE,"CHARSET = \"%s\"\n",
		charset_types[masterparam.charset]);
        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);
			masterparam.opt[optnr] = optval;
		}

                if (strncmp("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 (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 V%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;
}


/* build mkisofs-commandline */

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

	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 -jcharset ");
                strcat(tmp2,charset_types[masterparam.charset]);
                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,"-L ");
	}
	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 (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");
				break;
			case 3:
				strcat(tmp3," -no-boot");
				break;
			default:
				break;
			}	
		}
	}
	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");
			}
	
			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,"-P \"%s\" ",
			convert_escape(tmptmp));
		strcat(tmp4,tmp5);
	}	
	if (masterparam.preparer) {
		strcpy(tmptmp, masterparam.preparer);
		g_snprintf(tmp5,MAXLINE,"-p \"%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  */

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

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


/* get output of mkisofs (stderr) */

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

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

}


/* call mkisofs -print-size */

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

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

	dodebug(1, "spawning: %s\n",cmd);
	dolog(1, "Getting size of mkisofs-image");
	dolog(3, "Executing: %s\n", cmd);

	readcdda_pid = full_dpl_pipe3(&read_out,NULL,&read_err,cmd);

	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 blank 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) {
		gtk_text_insert(GTK_TEXT(readtrack_textview),
			NULL,NULL,NULL,text(396), strlen(text(396)));
		return 2;
	}

	return 0; 
}


/* get output of mkisofs */

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);
		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,text(402), tmp);
		gtk_label_set_text(GTK_LABEL(readtrack_info_label),tmp2);	

		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(403));	
	}


	/* 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);
					/* 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),
                NULL,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), text(400));

	/* build command line */
	build_mkisofs_cmdline(cmd, 0, masterparam.image_filename, 1);

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

	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), text(401));
		return 1;
	}

	/* all ok? */
	if (read_done == 0 && read_output_ctrl == 3) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(404));
	} else {
		/* some error? */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(405));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(406));	
	}

	return 0;
}


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

void mkisofs_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)pid, &ret, WNOHANG);

		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];
gchar outcmd[MAXLINE];
gint read_out, read_err, read_err2;
gint ret, tracknr;
pid_t pid1, pid2;

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

	strcpy(outcmd,"");
		
	/* set info label */
	gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(215));

	/* 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(write_devnr,tmp) != 0) {
		g_error("non existing cdrom?");
	}

	/* the writer does support burnproof? just enable it for now */
	if (does_support_burnproof()) {
		if (curset.writeburnfree) {
			strcpy(tmp4,"driveropts=burnfree");
		} else {
			strcpy(tmp4,"driveropts=noburnfree");
		}
	} else {
		strcpy(tmp4,"");
	}

	get_wrap_path("CDRECORD", tmp3);
	g_snprintf(cmd2, MAXLINE*10,
		"%s dev= \"%s\" fs=%dk -v %s ",  
		tmp3, tmp, setupdata.writer_fifo, tmp4);

	if (setupdata.writer_mode >= 0) {
		if (drivers[setupdata.writer_mode] != NULL) {
			g_snprintf(tmp,MAXLINE," driver=%s", 
				drivers[setupdata.writer_mode]->driver);
			strcat(cmd2,tmp);
		}
	}
	
	if (curset.writer_speed >= 0) {
		g_snprintf(tmp,MAXLINE," speed=%d", curset.writer_speed);	
		strcat(cmd2, tmp);
	}

	/* Ignore write-mode in multisession-mode */
/*
	if (curset.writemode == 0) {
		strcat(cmd2," -dao");
	}
	if (curset.writemode == 2) {
		strcat(cmd2," defpregap=0");
	}
	if (curset.writemode == 3) {
		strcat(cmd2," -raw96r");
	}
	if (curset.writemode == 4) {
		strcat(cmd2," -raw96p");
	}
	if (curset.writemode == 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");
	}

	/* now add track-specification: one data track */
	g_snprintf(tmp, MAXLINE, " 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),
		NULL,NULL,NULL,outcmd, strlen(outcmd));

	/* yes...command line is done */
	full_dpl_pipe4(&pid1,&pid2,NULL,&read_err,cmd,&read_out,&read_err2,cmd2);
	readcdda_pid = pid2;

	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, NULL);
	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 == 1) {
			cdrecord_reload = 0;
			show_dialog(ICO_WARN, text(277), T_OK, NULL, NULL, -1);
			
			/* now send CR to cdrecord to let it continue */
/*
			THIS IS A BUG IN CDRECORD BECAUSE NO WAY TO SEND
			THE CR WHEN IN PIPE MODE (because stdin is used)
			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, text(194),
				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), text(217));
		return 1;
	}

	/* cannot send cue sheet */
	if (read_output_ctrl == 4) {
		show_dialog(ICO_WARN,text(289),T_OK, NULL, NULL, 0);
	}

	/* aborted write ? */
	if (read_output_ctrl == 1) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(218));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(211));
		return 1;
	}

	/* error while reading ?*/
	if (read_done != 0) {
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(208));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(211));
		return 1;
	} else {
		/* all ok */
		gtk_label_set_text(GTK_LABEL(readtrack_info_label), text(209));
		gtk_label_set_text(GTK_LABEL(readtrack_small_info),text(212));
		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 (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, "Cdrecord-ProDVD-Clone");
		if (p != NULL) {
			*isProDVD = 1;
			p = strtok(p+22, " ");
			if (p != NULL) {
				strcpy(ver,p);
			}
		}
		if (strcmp(ver,"") == 0) {
			p = strstr(vline,"Cdrecord");
			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 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 line2[MAXLINE];
gchar ver[MAXLINE];
FILE *fpin;
gchar *p;

	strcpy(ver,"");
	get_wrap_path("CDDA2WAV",line);
	strcat(line," -h 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(11,"got: %s",line);
		if (strncmp(line, "Version", 7) == 0) {
			strcpy(line2, line);
			dodebug(10,"got version: %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 line2 contains the version string of cdda2wav */
	/* try to extract the version number */

	p = strstr(line2,"Version");
	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 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 which version of the shell be seem to have installed */
/* bash2 needs special handling */
/* return 1 if bash2, 0 else */

gint check_version_shell() {
gchar line[MAXLINE];
gchar ver[MAXLINE];
FILE *fpin;

	strcpy(line,"/bin/sh --version some_thing_that_never_executes 2>/dev/null");

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

	strcpy(ver,"");
        for (;;) {
                if (fgets(line,MAXLINE,fpin) == NULL) 
                        break;
		dodebug(10,"got: %s",line);
		if (strstr(line,"version") != NULL) {
			strcpy(ver,line);
			break;
		}
        }

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

	/* now ver contains the version string of the shell */
	if (strstr(ver,"bash, version 2") != NULL) {
		return 1;
	} else {
		return 0;
	}
}


/* 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) {
struct stat buf;
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar bin[MAXLINE];
gint groupaddok;
gchar *cmd_chown, *cmd_chgrp, *cmd_chmod;
nonroot_flags_t *entry;
GList *nrs_vals, *loop;

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

	/* check if basic commands are available */
	cmd_chown = get_chown_cmd();
	if (!cmd_chown) {
		strcat(ret,"\nError: No chown command found");
		return 1;
	}
	cmd_chgrp = get_chgrp_cmd();
	if (!cmd_chgrp) {
		strcat(ret,"\nError: No chgrp command found");
		return 1;
	}
	cmd_chmod = get_chmod_cmd();
	if (!cmd_chmod) {
		strcat(ret,"\nError: No chmod command found");
		return 1;
	}

	/* activate non-root-mode */
	if (mode == 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) {
			strcat(ret,"No groupadd command found");
			/* abort, without group its no way for us */
			return 1;
		} else {
			/* create command to create new group */
			g_snprintf(tmp,MAXLINE,"%s %s\n", GROUPADD, NONROOTMODEGROUP);
			strcat(ret,tmp);
		}	
	 }

	
	 /* 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);
	 g_snprintf(tmp,MAXLINE,"%s %s %s\n", cmd_chgrp, NONROOTMODEGROUP, bin);
	 strcat(ret,tmp);
	 g_snprintf(tmp,MAXLINE,"%s 2755 %s\n", cmd_chmod, bin);
	 strcat(ret,tmp);
 
	 /* 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(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);

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

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


/* save the current file owners and permissions to a file */

gint save_current_nonrootvalues(gchar *confdir, gchar *fname) {
gchar bin[MAXLINE];
gchar bin2[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 V%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;
	
}


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

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


/* reads the .xinf file for a given track and writes a new .inf file
   with the updated cd-text information */

void edit_xinf_for_cdtext(gchar *trackfile, gchar *tocfile) {
gchar xinfname[MAXLINE], infname[MAXLINE], tmpinfname[MAXLINE];
gchar tmp[MAXLINE];
gchar *p;
FILE *fd, *fd2;
gchar line[MAXLINE];
gchar id[MAXLINE];
gchar value[MAXLINE];
gchar title[MAXLINE], artist[MAXLINE], cddbtitle[MAXLINE];
gchar cdtitle[MAXLINE], cdartist[MAXLINE];
time_t acttime;
gchar timestr[MAXLINE];
gint found, found2, found3, found4;

/*
	g_print("tocfile: %s, file: %s\n", tocfile, trackfile);
*/
	/* get current time */
	acttime = time((time_t *) 0);
	strcpy(timestr,ctime(&acttime));
	timestr[strlen(timestr)-1] = '\0';

	/* generate xinf and inf filenames */
	strncpy(tmp,trackfile,MAXLINE);
	p = rindex(tmp,'.');
	if (!p) return;
	*p = '\0';
	strcpy(xinfname,tmp);
	strcpy(infname,tmp);
	strcat(xinfname,XCDROAST_INFO_EXT);
	strcat(infname,".inf");
	g_snprintf(tmpinfname,MAXLINE,"%s-%d", infname, (gint) getpid());	

	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 */
/*
	g_print("%s, %s, %s, %s\n", title, artist, cdtitle, cdartist);	
*/
	/* 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 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) {
			g_snprintf(tmp,MAXLINE,"cdtitle = \"%s / %s\"\n", convert_escape(title), convert_escape(artist));
			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 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);
        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);
	        g_snprintf(tmp,MAXLINE,"title = \"%s\"\n",convert_escape(title));
	        fputs(tmp,fd);
	        g_snprintf(tmp,MAXLINE,"artist = \"%s\"\n",convert_escape(artist));
     	   	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) {
                        g_snprintf(tmp,MAXLINE,"title = \"%s\"\n", convert_escape(title));
                        fputs(tmp, fd2);
                        found = 1;
                        continue;
                }

                /* replace the line with the artist */
                if (strncmp(line,"artist", 6) == 0) {
                        g_snprintf(tmp,MAXLINE,"artist = \"%s\"\n", convert_escape(artist));
                        fputs(tmp, fd2);
                        found2 = 1;
                        continue;
                }

                fputs(line,fd2);
        }
	/* lines missing? add them */
	if (found == 0) {
        	g_snprintf(tmp,MAXLINE,"title = \"%s\"\n", convert_escape(title));
                fputs(tmp, fd2);
	}
	if (found2 == 0) {
        	g_snprintf(tmp,MAXLINE,"artist = \"%s\"\n", convert_escape(artist));
        	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 */

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];
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,text(489), 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) {
				g_snprintf(tmp,MAXLINE,"ADD1 = \"%s\"\n", convert_escape(mstr->mstr_path));
			} else 
			if (mstr->mstr_path && mstr->redir_path) {
				g_snprintf(tmp,MAXLINE,"ADD2 = \"%s\",\"%s\"\n", convert_escape(mstr->mstr_path), convert_escape(mstr->redir_path));
			}
        		fputs(tmp,fd);
		}
		loop = loop->next;
	}

        /* get exclude-dirs */
        loop = g_list_first(masterparam.exclude_paths);
        while (loop) {
                excl = (gchar *)loop->data;
		if (excl) {
			g_snprintf(tmp,MAXLINE,"EXCL = \"%s\"\n", convert_escape(excl));
        		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);
	p = rindex(tmp,'.');
	if (!p) return 1;
	*p = '\0';
	strcpy(tmp2,tmp);
	strcat(tmp2,XCDROAST_INFO_EXT);

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

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

