/*
	xtools.c
	27.3.99 tn
*/

#include "largefile.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <errno.h>
#include <ctype.h>
#include <grp.h>
#include <pwd.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include "xcdrdata.h"
#include "xcdroast.h"
#include "main.h"

extern gint debug;
extern scsi_devices_t **scsidevices;
extern gchar **alt_scsidevices;
extern setup_data_t setupdata;
extern cd_info_t cdinfo;
extern track_info_t **trackinfo;
extern GList *imagelist;
extern current_set_t curset;
extern track_read_set_t trackreadset;
extern GList *tocfiles;
extern GList *writelist;
extern gint bigfonts;
extern gint oldfontcode;
extern gchar sharedir[MAXLINE];
extern gchar prefixdir[MAXLINE];
extern writer_driver_t **drv_options;
extern gint runningbash2;

void define_tooltip(GtkWidget *widget, gchar *ttext) {
GtkTooltips *tip;

#ifdef YELLOW_TIPS
GdkColor bg;
GtkStyle *style;
#endif
	/* tooltips wanted? */
	if (setupdata.option_tooltips == 0) {
		return;
	}

	tip = gtk_tooltips_new();
	gtk_tooltips_set_tip(tip,widget,ttext,NULL);

	/* set tip color (yellow) */

	/*
	*** Commented by C.W.Huang :
	*** Why set tip color by hand? It should be set by the theme.
	*** Due to an unknown feature or bug of gtk_widget_set_style(),
	*** GdkFont of the tip will be changed so multibyte characters
	*** cannot be displayed correctly!
	*/
#ifdef YELLOW_TIPS

	gtk_tooltips_force_window(tip);
	if (!gdk_color_parse(TOOLTIPCOL,&bg)) {
		g_warning("Can't parse color %s\n",TOOLTIPCOL);
		return;
	}
        if (!gdk_color_alloc(gtk_widget_get_colormap(tip->tip_window),&bg)) {
		g_warning("Cant allocate color %s\n",TOOLTIPCOL);
		return;
	}
	style = gtk_style_copy(gtk_widget_get_style(tip->tip_window));
	style->bg[GTK_STATE_NORMAL] = bg;
	gtk_widget_set_style(tip->tip_window, style);
#endif
}


/* sets the font and color for a label widget.
   if color/font is NULL then don't set color/font  */

void set_font_and_color(GtkWidget *widget, gchar *font, gchar *color) {
GtkStyle *style;
GdkColor c;
gchar tmp[MAXLINE];
gchar *p;

	style=gtk_style_copy(gtk_widget_get_style(widget));

	if (font != NULL) {
		gdk_font_unref(style->font);

		/* make copy of string, because we use strtok later */
		strcpy(tmp,font);

		/* for unknown reasons in some locales the fonts do
		   not work correcly bold or italic when using fontset */
		if (oldfontcode) {

			/* now use only the definition up to the first comma */
			p = strtok(tmp,",");
			if (p) {			
				style->font = gdk_font_load(p);
			} else {
				style->font = gdk_font_load(tmp);
			}
		} else {
			style->font = gdk_fontset_load(tmp);
		}

        	/* check if valid font */
        	if (style->font == NULL) {
                	g_warning("Font %s not found\n",tmp);
                	return;
        	}
	}

	if (color != NULL) {
		if (!gdk_color_parse(color,&c)) {
			g_warning("Can't parse color %s\n",color);
			return;
		}

        	if (!gdk_color_alloc(gtk_widget_get_colormap(widget),&c)) {
			g_warning("Cant allocate color %s\n",color);
			return;
		}

		style->fg[GTK_STATE_NORMAL] = c;
	}
	
	gtk_widget_set_style(GTK_WIDGET(widget),style);
}


/* sets the color for a label widget.
   if color is NULL then don't set color/font  */

void set_labelcolor(GtkWidget *widget, gchar *color) {
GtkStyle *style;
GdkColor c;

	style=gtk_style_copy(gtk_widget_get_style(widget));

	if (color != NULL) {
		if (!gdk_color_parse(color,&c)) {
			g_warning("Can't parse color %s\n",color);
			return;
		}

        	if (!gdk_color_alloc(gtk_widget_get_colormap(widget),&c)) {
			g_warning("Cant allocate color %s\n",color);
			return;
		}

		style->fg[GTK_STATE_NORMAL] = c;
	}
	
	gtk_widget_set_style(GTK_WIDGET(widget),style);
}


/* sets a certain row in a clist to a font */

void set_clist_row_font(GtkCList *clist, gint row, gchar *ffont) {
GtkStyle *style;
gchar tmp[MAXLINE];
gchar *p;

	style=gtk_style_copy(gtk_widget_get_style(GTK_WIDGET(clist)));

	/* make copy of string, because we use strtok later */
	strcpy(tmp,ffont);

	gdk_font_unref(style->font);
	/* for unknown reasons in some locales the fonts do
	   not work correcly bold or italic when using fontset */
	if (oldfontcode) {
		/* now use only the definition up to the first comma */
		p = strtok(tmp,",");
		if (p) {			
			style->font = gdk_font_load(p);
		} else {
			style->font = gdk_font_load(tmp);
		}
	} else {
		style->font = gdk_fontset_load(tmp);
	}

       	/* check if valid font */
       	if (style->font == NULL) {
               	g_warning("Font %s not found\n",tmp);
               	return;
       	}

	gtk_clist_set_row_style(clist,row,style);
}


/* colors a certain row in a clist */

void set_clist_row_color(GtkCList *clist, gint row, gchar *color) {
GtkStyle *style;
GdkColor c;

	style=gtk_style_copy(gtk_widget_get_style(GTK_WIDGET(clist)));

	if (!gdk_color_parse(color,&c)) {
		g_warning("Can't parse color %s\n",color);
		return;
	}

       	if (!gdk_color_alloc(gtk_widget_get_colormap(GTK_WIDGET(clist)),&c)) {
		g_warning("Cant allocate color %s\n",color);
		return;
	}
	style->fg[GTK_STATE_NORMAL] = c;

	gtk_clist_set_row_style(clist,row,style);
}


/* free a simple glist structure */

void free_glist(GList **list) {
GList *loop;
gchar *dir;

	loop = g_list_first(*list);
	while(loop) {
		dir = loop->data;
		g_free(dir); 
		loop = loop->next;
	}
	g_list_free(*list);
	*list = NULL;	
}


/* copy a simple glist (which has only strings as elements) */

void copy_glist(GList **dst, GList *src) {
GList *loop;
gchar *dir;

	/* clear target list */
	free_glist(dst); 

	loop = g_list_first(src);
	while(loop) {
		dir = loop->data;
		*dst = g_list_append(*dst,g_strdup(dir)); 
		loop = loop->next;
	}
}


/* remove a string-element from a glist */

void del_glist_link(GList **list, gchar *str) {
GList *loop;
gchar *dir;

	if (str == NULL) 
		return;

	loop = g_list_first(*list);
	while(loop) {
		dir = loop->data;
		if (dir && strcmp(str,dir) == 0) {
			g_free(dir); 
			*list = g_list_remove_link(*list, loop);
			return;
		}
		loop = loop->next;
	}
}


/* check if a string-element is in a glist */
/* return 1 if found, 0 if not */

gint check_in_glist(GList **list, gchar *str) {
GList *loop;
gchar *dir;

	if (str == NULL) 
		return 0;

	loop = g_list_first(*list);
	while(loop) {
		dir = loop->data;
		if (dir && strcmp(str,dir) == 0) {
			return 1;
		}
		loop = loop->next;
	}
	return 0;
}


/* check if a path is in a master-path-glist */
/* return 1 if found, 0 if not */

gint check_in_mstr_glist(GList **list, gchar *str) {
GList *loop;
mstr_redirect_t *mstr;
gchar *dir;

	if (str == NULL) 
		return 0;

	loop = g_list_first(*list);
	while(loop) {
		mstr = (mstr_redirect_t *) loop->data;
		if (mstr) {
			dir = mstr->mstr_path;
		} else {
			dir = NULL;
		}
		if (dir && strcmp(str,dir) == 0) {
			return 1;
		}
		loop = loop->next;
	}
	return 0;
}


/* remove a string-element from a master-glist */
/* if there is a redir-path, remove only this and return */
/* (unless del_both is set, where the whole entry is removed) */
/* del_both == -1, only remove link if possible */

void del_mstr_glist_link(GList **list, gchar *str, gint del_both) {
GList *loop;
mstr_redirect_t *mstr;
gchar *dir;

	if (str == NULL) 
		return;

	loop = g_list_first(*list);
	while(loop) {
		mstr = (mstr_redirect_t *) loop->data;
		if (mstr) {
			dir = mstr->mstr_path;
		} else {
			dir = NULL;
		}
		if (dir && strcmp(str,dir) == 0) {
			/* found link */
			if (mstr->redir_path) {
				/* remove redir-path only */
				g_free(mstr->redir_path);
				mstr->redir_path = NULL;

				if (del_both == 0)
					return;
			}
			if (del_both == -1) return;

			g_free(dir); 
			g_free(mstr);
			*list = g_list_remove_link(*list, loop);
			return;
		}
		loop = loop->next;
	}
}


/* clear a mstr_glist */

void clear_mstr_glist(GList **list) {
GList *loop;
mstr_redirect_t *mstr;

	loop = g_list_first(*list);
	while(loop) {
		mstr = (mstr_redirect_t *) loop->data;
		if (mstr) {
			if (mstr->mstr_path) 
				g_free(mstr->mstr_path);
			if (mstr->redir_path) 
				g_free(mstr->redir_path);
			g_free(mstr);
		}
		loop = loop->next;
	}
	g_list_free(*list);
	*list = NULL;	
}


/* add a redir path to the master-glist */

void add_redir_mstr_glist(GList **list, gchar *str, gchar *new) {
GList *loop;
mstr_redirect_t *mstr;
gchar *dir;

	if (str == NULL) 
		return;

	loop = g_list_first(*list);
	while(loop) {
		mstr = (mstr_redirect_t *) loop->data;
		if (mstr) {
			dir = mstr->mstr_path;
		} else {
			dir = NULL;
		}
		if (dir && strcmp(str,dir) == 0) {
			/* found link */
			if (mstr->redir_path) {
				/* remove redir-path first */
				g_free(mstr->redir_path);
			}
			/* now set new value */
			mstr->redir_path = g_strdup(new);
			return;
		}
		loop = loop->next;
	}
}


/* get a string in the form bla => foo and return only bla */

void extract_mstr_path_from_clist(gchar *in, gchar *out) {
gint i, found;

	if (in == NULL || out == NULL) 
		return;

	found = -1;

	for (i = 0; i < strlen(in)-1; i++) {
		if ((in[i] == '=') && (in[i+1] == '>')) {
			found = i;
			break;
		}
	} 

	if (found == -1) {
		/* nothing found - return original string */
		strcpy(out,in);
	} else {
		strncpy(out,in,found);
		out[found] = '\0';
	}
	strip_string(out);
}


/* get the redir path from the master-glist */

void get_redir_path_from_mstr_glist(GList **list, gchar *str, gchar *ret) {
GList *loop;
mstr_redirect_t *mstr;
gchar *dir;

	if (str == NULL) 
		return;

	loop = g_list_first(*list);
	while(loop) {
		mstr = (mstr_redirect_t *) loop->data;
		if (mstr) {
			dir = mstr->mstr_path;
		} else {
			dir = NULL;
		}
		if (dir && strcmp(str,dir) == 0) {
			/* found link */
			if (mstr->redir_path) {
				strcpy(ret, mstr->redir_path);
			} else {
				strcpy(ret, "");
			}
			return;
		}
		loop = loop->next;
	}
	strcpy(ret, "");
}


/* set scsi-sector-size for a given device */

void set_sectorsize(gint devnr, gint size) {
gint i;

        i = 0;
        while(scsidevices[i] != NULL) {
                if (devnr == scsidevices[i]->devnr) {
                        scsidevices[i]->sector_size = size;
                        return;
                }
                i++;
        }
}


/* get scsi-sector-size for a given device */

gint get_sectorsize(gint devnr) {
gint i;

        i = 0;
        while(scsidevices[i] != NULL) {
                if (devnr == scsidevices[i]->devnr) {
                        return(scsidevices[i]->sector_size);
                }
                i++;
        }
 
        return DATASECTORSIZE;
}


/* convert the devnr to a device-string. return 1 if devnr not found */

gint convert_devnr2devstring(gint devnr, gchar *str) {
gint i;
gchar tmp[MAXLINE];

	i = 0;
	while(scsidevices[i] != NULL) {
		if (devnr == scsidevices[i]->devnr) {
			g_snprintf(tmp,MAXLINE,"%s %s [%d,%d]",
				scsidevices[i]->vendor, scsidevices[i]->model,
				scsidevices[i]->bus, scsidevices[i]->id);
			strcpy(str,tmp);
			return 0;
		}
		i++;
	}

	strcpy(str,"");
	return 1;
}


/* convert the devnr to a vendor-string. return 1 if devnr not found */

gint convert_devnr2vendor(gint devnr, gchar *str) {
gint i;

	i = 0;
	while(scsidevices[i] != NULL) {
		if (devnr == scsidevices[i]->devnr) {
			strcpy(str,scsidevices[i]->vendor);
			return 0;
		}
		i++;
	}
	strcpy(str,"");
	return 1;
}


/* convert the devnr to a model-string. return 1 if devnr not found */

gint convert_devnr2model(gint devnr, gchar *str) {
gint i;

	i = 0;
	while(scsidevices[i] != NULL) {
		if (devnr == scsidevices[i]->devnr) {
			strcpy(str,scsidevices[i]->model);
			return 0;
		}
		i++;
	}
	strcpy(str,"");
	return 1;
}


/* convert the devnr to a bus/id/lun-string. return 1 if devnr not found */

gint convert_devnr2busid(gint devnr, gchar *str) {
gint i;

	i = 0;
	while(scsidevices[i] != NULL) {
		if (devnr == scsidevices[i]->devnr) {
			if (scsidevices[i]->alt_dev == -1) {
				g_snprintf(str,MAXLINE,"%d,%d,%d",
					scsidevices[i]->bus,
					scsidevices[i]->id,
					0);  /* lun always zero? */	
			} else {
				/* return manual configured device-str */
				strncpy(str,
				    alt_scsidevices[scsidevices[i]->alt_dev],
				    MAXLINE);
				convert_escape(str);
			}
			return 0;
		}
		i++;
	}
	strcpy(str,"");
	return 1;
}


/* convert kilobytes to MB/min string */
/* displays MB when using 2048b sectors. min like stored audiotracks on the
   hard drive - this is not correct when displaying the minutesize of DATA
   tracks. */ 
void convert_kbytes2mbminstring(gint kbytes, gchar *str) {
gint mb;
gint min;
gint sec;
gint frames;
gint frms;
gint64 tmpsize;

	mb = kbytes/1024;
	
#if 0
	/* we have a problem here, that we hit gint overflow on
	   large values - in this case round a little inexact */
	if (mb < 2000) {
		/* correct value */
		frames = (kbytes*1024)/CDDAFRAME;
	} else {
		/* rounded value */
		frames = (kbytes/CDDAFRAME)*1024;
	}	
#endif
	/* new code using 64 bit values */
	tmpsize = (gint64)kbytes * 1024;
	frames = (gint) ((gint64)tmpsize/CDDAFRAME);
	
	min = frames/(60*75);
	sec = (frames%(60*75))/75;
	frms = (frames%75);
	/* csec = (4*(frames%75)+1)/3; */

	g_snprintf(str,MAXLINE,"%dMB / %d:%02d.%02d",mb,min,sec,frms);
}


/* convert kilobytes to MB/min string */
/* displays MB when using 2048b sectors. min like the size of track after
   burned. The only correct min size display when burning data tracks */ 

void convert_kbytes2mbcorrectminstring(gint kbytes, gchar *str) {
gint mb;
gint min;
gint sec;
gint frames;
gint frms;

	mb = kbytes/1024;
	frames = kbytes/2;	
	min = frames/(60*75);
	sec = (frames%(60*75))/75;
	frms = (frames%75);
	/* csec = (4*(frames%75)+1)/3; */

	g_snprintf(str,MAXLINE,"%dMB / %d:%02d.%02d",mb,min,sec,frms);
}


/* convert frames to MB/min string */
/* should only be used for audio or full disk info */

void convert_frames2mbminstring(gint frames, gchar *str) {
gint mb;
gint min;
gint sec;
gint frms;
gint64 tmpsize;

#if 0
	mb = ((frames/1024)*CDDAFRAME)/1024; 
	/* mb = ((frames/1024)*DATASECTORSIZE)/1024; */
#endif
	/* use 64 bit values */
	tmpsize = (gint64)frames * CDDAFRAME;
	mb = (gint) ((gint64)tmpsize >> 20);

	min = frames/(60*75);
	sec = (frames%(60*75))/75;
	frms = (frames%75);
	/* csec = (4*(frames%75)+1)/3; */

	g_snprintf(str,MAXLINE,"%dMB / %d:%02d.%02d",mb,min,sec,frms);
}


/* convert frames/sectors to MB string */
/* note - this is only true for DATA-tracks */

void convert_frames2mbstring(gint frames, gchar *str) {
gint mb;

	mb = frames*(DATASECTORSIZE/1024)/1024;
	g_snprintf(str,MAXLINE,"%dMB",mb);
}


/* convert kbytes to MB string */

void convert_kbytes2mbstring(gint kbytes, gchar *str) {
gint mb;

	mb = kbytes/1024;
	g_snprintf(str,MAXLINE,"%dMB",mb);
}


/* convert frames to min string */

void convert_frames2minstring(gint frames, gchar *str) {
gint min;
gint sec;
gint frms;

	min = frames/(60*75);
	sec = (frames%(60*75))/75;
	frms = (frames%75);
	/* csec = (4*(frames%75)+1)/3; */

	g_snprintf(str,MAXLINE,"%d:%02d.%02d",min,sec,frms);
}


/* creates a label that is right justified. Useful when
   packing into a table */

GtkWidget *rightjust_gtk_label_new(gchar *txt) {
GtkWidget *align;
GtkWidget *label;

	/* create right justify alignment */
	align = gtk_alignment_new(1.0,0.5,0,0);
	label = gtk_label_new(txt);
	gtk_container_add(GTK_CONTAINER(align),label);
	gtk_widget_show(label);

	return align;
}


/* creates a label that is left justified. Useful when
   packing into a table */

GtkWidget *leftjust_gtk_label_new(gchar *txt) {
GtkWidget *align;
GtkWidget *label;

	/* create left justify alignment */
	align = gtk_alignment_new(0.0,0.5,0,0);
	label = gtk_label_new(txt);
	gtk_container_add(GTK_CONTAINER(align),label);
	gtk_widget_show(label);

	return align;
}


/* get some info about our image-file */

void analyze_imgfile(gchar *path, gchar *file, GList **retlist) {
struct stat buf;
image_files_t *entry;
gchar tmp[MAXLINE];
gchar volid[MAXLINE];
off_t size;
gint type,readable,isosize;
gint fd;

	strncpy(tmp,path,MAXLINE-strlen(file)-2);
	strcat(tmp,"/");
	strcat(tmp,file);

	stat(tmp,&buf);

	/* check if regular file or link */
	if (S_ISLNK(buf.st_mode) != 1 && S_ISREG(buf.st_mode) != 1) {
		/* its not..so ignore */
		return;
	} 

	/* readable for us? */
	fd = open(tmp, O_RDONLY,0);
	if (fd == -1) {
		readable = 0;
	} else {
		readable = 1;
		close(fd);
	}	
	
	size = (off_t) buf.st_size;

	isosize = 0;

	/* now do some tests about file-contents */
	if (strncmp(file+strlen(file)-4,".toc",4) == 0) {
		type = 4;
	} else if (strncmp(file+strlen(file)-4,".wav",4) == 0) {
		/* wav-file */
		if (check_wav_file(tmp) == 0) {
			/* invalid wav */
			type = 2;
		} else {
			/* valid wav */
			type = 1;
		}
	} else {
		/* data-file */
		isosize = check_iso_file(-1,tmp,volid,0);
		if (isosize == 0) {
			/* unknown data */
			type = 3;
		} else {
			/* iso9660 */
			type = 0;
		}
	}

	/* allocate memory and fill structure */
	entry = g_new(image_files_t,1);
	entry->path = g_strdup(tmp);
	entry->mtime = buf.st_mtime;
	entry->size = (off_t) size;
	entry->type = type;
	entry->readable = readable;
	entry->from_track = 0;
	if (type == 0) {
		entry->volname = g_strdup(volid);
	} else {
		entry->volname = NULL;
	}
	entry->title = NULL;
	entry->artist = NULL;
	entry->cddb_ttitle = NULL;
	entry->cd_discid = NULL;
	
	entry->isosize = isosize;
	entry->last_session_start = -1;
	entry->next_session_start = -1;

	/* find if there is some information in the inf-file */
	get_inf_tracktitle(tmp, entry);

	/* add to list */
	*retlist = g_list_append(*retlist, entry);
}


/* scans a directory for files matching the known extensions */
/* return 0 if ok, 1 on problem */

gint get_img_files(gchar *path, GList **retlist) {
gchar *img_ext[] = IMG_EXTENSIONS;
struct dirent *ent;
DIR *dir;         
gint i,len,len2;

	dir = opendir(path);

	/* invalid directory */
	if (dir == NULL) 
		return 1;

	/* scan a directory */
	while ( (ent = readdir(dir)) ) {
		/* does the extension match? */
		for(i = 0; img_ext[i] != NULL; i++) {
			len = strlen(img_ext[i]);
			len2 = strlen(ent->d_name);

			/* skip to short filenames */
			if (len2 < len) continue;

			if (strncmp((ent->d_name)+len2-len,img_ext[i],len) == 0) {
				/* we found a match */
				analyze_imgfile(path,ent->d_name,retlist);			
			}
		}		
	}

	closedir(dir);
	return 0;
}


/* print imagelist-memory-structure (debug purpose) */

void print_imagelist() {
GList *loop;
image_files_t *entry;

	dodebug(2,"--------- imagelist glist ---------\n");
	loop = g_list_first(imagelist);
	while (loop) {
		entry = loop->data;
		dodebug(2,"path: %s, %lld, %d, %d, %d (%d,%d)\n",entry->path,
			(gint64)entry->size, entry->type, entry->readable,
			entry->isosize, entry->last_session_start, 
			entry->next_session_start);
		if (entry->volname != NULL) {
			dodebug(2, "\tvolname: %s\n", entry->volname);
		}

		loop = loop->next;
	}	
}


/* sort the imagelist according to file names */

void sort_imagelist() {
GList *first, *last, *list1, *list2;
image_files_t *ent1, *ent2, *ent3;

       first = g_list_first(imagelist);
       last = g_list_last(imagelist);
       for (list1 = first; list1 != last; list1 = list1->next) {
               for (list2 = last; list2 != list1; list2 = list2->prev) {
                       ent1 = (image_files_t *) list1->data;
                       ent2 = (image_files_t *) list2->data;

                       if(strcmp(ent1->path,ent2->path) > 0) {
                               ent3 = ent1;
                               list1->data = list2->data;
                               list2->data = ent3;
                       }
               }
       }
}


/* search all image-directories and create a list of matching files */
/* return number of matching files */

gint scan_imagedirs() {
GList *loop;
gchar tmp[MAXLINE];
image_files_t *entry;

	/* free the old image-list first */
	loop = g_list_first(imagelist);
	while (loop) {
		entry = loop->data;
		g_free(entry->path);
		g_free(entry->volname);
		g_free(entry->title);
		g_free(entry->artist);
		g_free(entry->cddb_ttitle);
        	g_free(entry->cd_discid);
		g_free(entry);

		loop = loop->next;
	}
	g_list_free(imagelist);
	imagelist = NULL;

	loop = g_list_first(setupdata.image_dirs);
	while (loop) {
		/* image-dir extracted */
		strncpy(tmp,(gchar *)loop->data, MAXLINE);
		get_img_files(tmp,&imagelist);

		loop = loop->next;
	}

	/* sort the image-list */
	sort_imagelist();

	/* now we have a complete image-list */
	if (debug) print_imagelist(); 

	return (g_list_length(imagelist));
}


/* return a string saying which type of CD we are currently handling */
/* mode = 0: used check cd in drive, mode = 1: check trackreadset for
   writing */

gint determine_cd_type(gchar *ret, gint mode) {
gint i;
gint audio,data;
gint type;
gchar tmp[MAXLINE];
GList *loop;
track_read_param_t *trackparam;
	
	/* unknown type */
	type = -1;

	/* count tracks */
	audio = 0;
	data = 0;
	trackparam = NULL;
	
	
	if (mode == 0) {
		/* check cdinfo-structure */
		for (i = 0; i < cdinfo.nr_tracks; i++) {
			if (trackinfo[i]->type == 0) {
				data++;
			} else {
				audio++;
			}
		}
	} else {
		/* check trackreadset-structure */
		loop = g_list_first(trackreadset.trackparams);
		while(loop) {
			trackparam = loop->data;
			if (trackparam->tracktype == 0) {
				data++;
			} else {
				audio++;
			}

			loop = loop->next;
		}
		/* now point trackparam back to first track for later use */
		loop = g_list_first(trackreadset.trackparams);
		if (loop) trackparam = loop->data;
		else trackparam = NULL;
	}	

	/* pure data-cd */
	if (data == 1 && audio == 0) {
		type = 0;
	} else
	/* pure audio-cd */
	if (data == 0 && audio > 0) {
		type = 1;
	} else
	/* mixed-mode */
	if (mode == 0 && data == 1 && audio > 0 && trackinfo[0]->type == 0) {
		type = 2;
	} else
	if (mode == 1 && data == 1 && audio > 0 && trackparam->tracktype == 0) {
		type = 2;
	} else
	/* cd-extra */
	if (mode == 0 && data == 1 && audio > 0 && cdinfo.have_cdextra) {
		type = 3;
	} else
	if (mode == 1 && data == 1 && audio > 0 && trackparam->tracktype == 1) {
		/* one data, at least one audio and first track audio */
		type = 3;
	} else 
	/* multisession */
	if (data > 1 && audio == 0) {
		type = 4;
	} 

	/* enough for now */
	switch (type) {
	case 0:
		strncpy(tmp,text(128),MAXLINE);
		break;
	case 1:
		strncpy(tmp,text(129),MAXLINE);
		break;
	case 2:
		strncpy(tmp,text(130),MAXLINE);
		break;
	case 3:
		strncpy(tmp,text(131),MAXLINE);
		break;
	case 4:
		strncpy(tmp,text(132),MAXLINE);
		break;
	default:
		strncpy(tmp,text(127),MAXLINE);
		break;
	}

	/* return value */
	strncpy(ret,tmp,MAXLINE);
	return (type);
}


/* calculate free space dependent of current image-dir setting */
/* return free kbytes and kbytes free in biggest imagedir */

gint determine_free_space(gint *biggestfree) {
gchar tmp[MAXLINE];
gchar path[MAXLINE];
GList *loop;
gint free, getfree;
gint maxfree;

	/* get image-path */
	if (curset.image_index == -1) {
		strncpy(path,"",MAXLINE);
	} else {
		strncpy(path,(gchar *)g_list_nth_data(setupdata.image_dirs,
			curset.image_index), MAXLINE);

		/* this dir writeable? */
		if (is_dir_writeable(path) == 1) {
			/* its not */
			*biggestfree = 0;
			return 0;
		}
	}

	free = 0;
	maxfree = 0;
	if (strcmp(path,"") != 0) {
		free = get_free_space(path,NULL);
		maxfree = free;
	} else {
		/* automatic setting - add all available space */
		loop = g_list_first(setupdata.image_dirs);
		while(loop) {
			strcpy(tmp,(gchar *)loop->data);

			/* this dir writeable? */
			if (is_dir_writeable(tmp) == 1) {
				/* no? skip */
				loop = loop->next;
				continue;
			}

			getfree = get_free_space(tmp,NULL);
			free += getfree;
			/* get biggest block */
			if (getfree > maxfree) 
				maxfree = getfree;
			loop = loop->next;
		}
	}

	if (free < 0) {
		g_warning("Invalid image-path setting?\n");
		free = 0;
		maxfree = 0;
	}

	*biggestfree = maxfree;
	return free;
}


/* does look where to save the tracks before reading them. 
   Checks available diskspace and the image-directory-settings.
   return 0 if ok, 1 on error/disk full, 2 if no writeable dir found */
/* return via call by reference the size (in kbytes) that will be
   free due overwriting old files. Also return the freed size on
   the directory with the most space available */

gint allocate_track_filenames(gint *overwrite, gint *overwritebiggest) {
gchar tmp[MAXLINE];
gchar biggestpath[MAXLINE];
gchar path[MAXLINE];
gchar ext[MAXLINE];
track_read_param_t *trackparam;
GList *loop, *loop2;
gint free;
gint size, tmpkbyte;
gint ret;
image_dir_free_t *freedir;
GList *freedirs;
struct stat buf;
gint overwritefree, overwritefreebiggest;
gint maxfree;

	dodebug(10,"calling allocate_track_filenames\n");

	overwritefree = 0;
	overwritefreebiggest = 0;
	ret = 0;
	freedirs = NULL;
	maxfree = 0;
	strcpy(biggestpath,"");

	/* build image-path/free structure */
	if (curset.image_index == -1) {
		/* automatic setting */
		loop = g_list_first(setupdata.image_dirs);
		while(loop) {
			strcpy(path,(gchar *)loop->data);

			/* this dir writeable? */
			if (is_dir_writeable(path) == 1) {
				/* no? skip */
				loop = loop->next;
				continue;
			}
			free = get_free_space(path,NULL);
			freedir = g_new(image_dir_free_t,1);
			freedir->path = g_strdup(path);
			freedir->free = free;
			freedirs = g_list_append(freedirs,freedir);

			/* path with biggest available block? */
			if (free > maxfree) {
				maxfree = free;
				strcpy(biggestpath,path);
			}	
			loop = loop->next;
		}
		/* no dirs writeable */
		if (freedirs == NULL) {
			*overwrite = 0;
			*overwritebiggest = 0;
			return 2;
		}
	} else {
		/* single path */
		strncpy(path,(gchar *)g_list_nth_data(setupdata.image_dirs,
			curset.image_index), MAXLINE);
		/* this dir writeable? */
		if (is_dir_writeable(path) == 1) {
			*overwrite = 0;
			*overwritebiggest = 0;
			return 2;
		}
		free = get_free_space(path,NULL);
		freedir = g_new(image_dir_free_t,1);
		freedir->path = g_strdup(path);
		freedir->free = free;
		freedirs = g_list_append(freedirs,freedir);
		maxfree = free;
		strcpy(biggestpath,path);
	}
	/* now we have a structure with all path we are allowed to 
	   save data in and how much space is available there */

	/* loop through all available tracks */
	loop = g_list_first(trackreadset.trackparams);
	while (loop) {
		trackparam = loop->data;

		if (trackparam->tracktype == 0) 
			strcpy(ext,"img");
		else
			strcpy(ext,"wav");
		
		/* how much space needs this track? */
		size = trackparam->kbyte;

		strcpy(path,"");

		/* where is enough space for it? */
		loop2 = g_list_first(freedirs);
		while (loop2) {
			freedir = loop2->data;
		
			/* build temporary filename */
			g_snprintf(tmp,MAXLINE, "%s/%s-%02d.%s", freedir->path, 
				curset.file_prefix,
				trackparam->starttrack, ext);

			/* already a file with this name on hd? */
			if (stat(tmp,&buf) == 0) {
				/* file exists */
				tmpkbyte = (gint) ((off_t)buf.st_size >> 10);
				overwritefree += tmpkbyte;

				/* file in directory with most space? */
				if (strcmp(freedir->path,biggestpath) == 0) {
					overwritefreebiggest += tmpkbyte;
				} 
			} else {
				tmpkbyte = 0;
			}

			/* enough free? consider space that is freed
			   when we overwrite a file (tmpkbyte) */
			if (size < (freedir->free + tmpkbyte)) {
				/* found freespace */
				strcpy(path,freedir->path);
				freedir->free-=size - tmpkbyte;
				break;
			}
 
			loop2 = loop2->next;
		}
	
		/* no free space found? */
		if (strcmp(path,"") == 0) {
			/* mark we found an error */
			ret = 1;
		}

		/* tmp does contain now our valid filename */
		g_free(trackparam->trackfile);
		trackparam->trackfile = g_strdup(tmp);

		loop = loop->next;
	}

	/* free image-path/free structure */
	loop2 = g_list_first(freedirs);
	while (loop2) {
		freedir = loop2->data;
		g_free(freedir->path);
		g_free(freedir);
		loop2 = loop2->next;
	}
	g_list_free(freedirs);

	*overwrite = overwritefree;
	*overwritebiggest = overwritefreebiggest;

	if (debug > 1) {
		print_trackreadset();
	}

	return ret;
}


/* does scan the image-structure for toc-files.
   Takes current image-dir-setting into account. Return number 
   of found toc files or 0. Newest file is on top */ 

gint scan_for_toc_files() {
GList *loop;
image_files_t *entry;
gchar basename[MAXLINE];
gchar ipath[MAXLINE];
gchar *p;
time_t fdate;

	/* clear old list */
	g_list_free(tocfiles);
	tocfiles = NULL;
	fdate = 0;

	loop = g_list_first(imagelist);
	while (loop) {
		entry = loop->data;

		/* toc-file */
		if (entry->type == 4) {
		
			/* get the basedir */
			strncpy(basename,entry->path,MAXLINE);
			p = rindex(basename,'/');
			*p = '\0';
			if (strcmp(basename,"") == 0) {
				strcpy(basename,"/");
			}
		
			/* now check if the basedir fits in the currently
			   set image-path */
			if (curset.image_index != -1) {
				strncpy(ipath, (gchar *)g_list_nth_data(
					setupdata.image_dirs,
					curset.image_index), MAXLINE);

				/* does not fit - skip */
				if (strcmp(ipath, basename) != 0) {
					loop = loop->next;
					continue;
				}
			}
			
			/* if new file newer than the old one */
			if (entry->mtime < fdate) {
				/* append at back */
				tocfiles = g_list_append(tocfiles,entry->path);
			} else {
				/* prepend at front */
				tocfiles = g_list_prepend(tocfiles,entry->path);
				fdate = entry->mtime;
			}
		}

		loop = loop->next;
	}

	return g_list_length(tocfiles);
}


/* this function is called whenever a dialog window idles on the screen
   and we want that events are processed and if there are no events
   no CPU-time is wasted */

void wait_and_process_events() {

	while (gtk_events_pending())
		gtk_main_iteration();
	usleep(100);
}


/* check if all files scheduled for writing does exist and have
   the right size. Return 0 if all ok, 1 if all files there but with
   wrong size, 2 if files missing and 3 if no permission to read/invalid */

gint check_write_files(gint nosizecheck) {
GList *loop;
track_read_param_t *trackparam;
struct stat buf;
off_t size;
gint sumframes;
gint fd;
gint errsize, diff;

	sumframes = 0;
	errsize = 0;
	loop = g_list_first(trackreadset.trackparams);
	while(loop) {
		trackparam = loop->data;

		if (stat(trackparam->trackfile, &buf) != 0) {
			/* no such file */
			return 2;
		}
		
		/* check if regular file or link */
		if (S_ISLNK(buf.st_mode) != 1 && S_ISREG(buf.st_mode) != 1) {
			/* its not */
			return 3;
		}

		/* readable for us? */
		fd = open(trackparam->trackfile, O_RDONLY,0);
		if (fd == -1) {
			return 3;
		} else {
			close(fd);
		}

		if (trackparam->tracktype == 0) {
			/* datatrack */
			size = (off_t) ((off_t)trackparam->frames * DATASECTORSIZE);
			sumframes += trackparam->frames;
		} else {
			/* audiotrack */
			size = (off_t) ((off_t)trackparam->frames * CDDAFRAME);
			sumframes += trackparam->frames;
		}

		/* check size of file - allow a offset of 4096 bytes */
		/* and offset of 152*2048 (leadout+runout sectors) */
		/* (and allow offset of 44 bytes (wavheader)) */
		diff = (gint) abs((off_t) size - (off_t) buf.st_size);
		if (diff != 0 && diff != 4096 && diff != 152*2048 && diff != 44) {
			/* a file with wrong size found? */
			errsize++;
		} 
		loop = loop->next;
	}	
	/* g_print("sumframes: %d\n", sumframes); */

	if (errsize == 0 || nosizecheck) {
		/* all ok */
		return 0;
	} else {
		/* files with wrong sizes */
		return 1;
	}
}


/* get the size of a track given by filename from imagelist (in bytes) */ 
/* or -1 when not found */

off_t get_size_from_imagelist(gchar *tname) {
GList *loop;
image_files_t *entry;

	loop = g_list_first(imagelist);
	while (loop) {
		entry = loop->data;

		if (strcmp(tname,entry->path) == 0) {
			return ((off_t) entry->size);
		}
		loop = loop->next;
	}

	return (off_t)-1;
}


/* get the type of a track given by filename from imagelist */ 
/* or -1 when not found */

gint get_type_from_imagelist(gchar *tname) {
GList *loop;
image_files_t *entry;

	loop = g_list_first(imagelist);
	while (loop) {
		entry = loop->data;

		if (strcmp(tname,entry->path) == 0) {
			return (entry->type);
		}
		loop = loop->next;
	}

	return -1;
}


/* get the number of a track given by filename from imagelist */ 
/* or -1 when not found */

gint get_tracknr_from_imagelist(gchar *tname) {
GList *loop;
image_files_t *entry;

	loop = g_list_first(imagelist);
	while (loop) {
		entry = loop->data;

		if (strcmp(tname,entry->path) == 0) {
			return (entry->from_track);
		}
		loop = loop->next;
	}

	return -1;
}


/* get the msinfo values from imagelist */

void get_msinfo_from_imagelist(gchar *tname, gint *nr1, gint *nr2) {
GList *loop;
image_files_t *entry;

	loop = g_list_first(imagelist);
	while (loop) {
		entry = loop->data;

		if (strcmp(tname,entry->path) == 0) {

			*nr1 = entry->last_session_start;
			*nr2 = entry->next_session_start;
		}
		loop = loop->next;
	}
}


image_files_t *get_entry_from_imagelist(gchar *tname) {
GList *loop;
image_files_t *entry;

	loop = g_list_first(imagelist);
	while (loop) {
		entry = loop->data;

		if (tname && strcmp(tname,entry->path) == 0) {
			return entry;
		}
		loop = loop->next;
	}
	return NULL;
}


/* get the discid of a track given by filename from imagelist */ 
/* or 1 when not found */

gint get_discid_from_imagelist(gchar *tname, gchar *ret) {
GList *loop;
image_files_t *entry;

	loop = g_list_first(imagelist);
	while (loop) {
		entry = loop->data;

		if (strcmp(tname,entry->path) == 0) {

			if (entry->cd_discid == NULL) 
				return 1;

			strcpy(ret, entry->cd_discid);
			return 0;
		}
		loop = loop->next;
	}

	return 1;
}


/* get the volname of a track given by filename from imagelist */ 
/* or 1 when not found */

gint get_volname_from_imagelist(gchar *tname, gchar *ret) {
GList *loop;
image_files_t *entry;

	loop = g_list_first(imagelist);
	while (loop) {
		entry = loop->data;

		if (strcmp(tname,entry->path) == 0) {

			if (entry->volname == NULL) 
				return 1;

			strcpy(ret, entry->volname);
			return 0;
		}
		loop = loop->next;
	}

	return 1;
}


/* is valid wav-file and in cd-quality? */
/* return 1 if, 0 if not */

gint check_wav_file(gchar *wavname) {
guchar waveHdr[44];
gint fd;

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

	read(fd, &waveHdr, sizeof(waveHdr));

	if (!is_std_wav_file(waveHdr)) {
		/* no wav at all */
		close(fd);
		return 0;
	}

	/* is it in cd-quality? */
	if (!is_in_cd_quality(waveHdr)) {
		close(fd);
		return 0;
	}

	/* passed all tests */
	close(fd);
	return 1;
}


/* small thing for iso-check */

gint empty(gchar c) {
	return (c == 0 || c == ' ');
}


/* check if valid iso9660-image */
/* return number of sectors if, 0 if not */
/* if isoname set to NULL then query drive directly */

gint check_iso_file(gint devnr, gchar *isoname, gchar *volid, gint startsec) {
gchar buf[DATASECTORSIZE];
gchar tmp[MAXLINE];
gchar c;
gint i,j,k,count;
gint volsize;
 
	if (isoname != NULL) {
		/* read from file */
		if (read_info_sector_from_file(isoname,buf,sizeof(buf)) == 0) {
			return 0;
		}
	} else {
		/* read from device */
		if (read_info_sector_from_dev(devnr,buf,sizeof(buf), startsec) == 0) {
			return 0;
		}
	}

	/* search iso9660-signature */
	if (strncmp(buf, "\001CD001\001", 8) != 0) {
		return 0;
	}

	/* ok, we got an iso9660-image. 
	   As a bonus extract volumne-name if requested */
	if (volid != NULL) {
		count = 0;
		for(i = 40; i < 72; i++) {
			if (empty(buf[i]))
				continue;
			for (j = i+1; j < 72; j++) {
				if (!buf[j] || (j < 72-1
				&& empty(buf[j]) && empty(buf[j+1])))
					break;
			}
			for (k = i; k < j; k++) {
				c = buf[k];
				if (isprint((gint)c) || isspace((gint)c)) {
					tmp[count++] = c;			
				}
			}
			i = j;
		}
		tmp[count] = '\0';
		strcpy(volid,tmp);
	}

	/* now also extract the size of the image */
	volsize = ((buf[80] & 0xff) |
 		  ((buf[81] & 0xff) << 8) |
 		  ((buf[82] & 0xff) << 16) |
 		  ((buf[83] & 0xff) << 24)); 
		   
	return volsize;
}


/* get cd toc and do read the iso9660-volid if possible */

void get_cd_toc_and_volid(gint devnr) {
gint i, volsize;
gchar tmp[MAXLINE];

	get_cd_toc(devnr);

	/* no cd loaded? */
	if (cdinfo.nr_tracks <= 0) {
		return;
	}
	strcpy(tmp,"");

	/* scan every data track */
#ifdef SCANEVERYTRACK 
	for (i = 0; i < cdinfo.nr_tracks; i++) {
#else
	for (i = 0; i < 1; i++) {
#endif
	
		if (trackinfo[i]->type == 0) {
			/* get iso-header for current track */
			strcpy(tmp,"");
			volsize = check_iso_file(devnr, NULL, tmp, 
				trackinfo[i]->start_sec); 	
			if (strcmp(tmp,"") != 0) {
				g_free(trackinfo[i]->volname);
				trackinfo[i]->volname = g_strdup(tmp);
			}
			trackinfo[i]->isosize = volsize;
		}
	}

	/* now set disk-title to iso9660-volname because thats all we
	   got at the moment */

	/* last label still in buffer? */
	if (strcmp(tmp,"") != 0) {
		/* now check we have currently another title */
		if (cdinfo.cddb_dtitle == NULL) { 
			/* no? then use iso-header as title */
			cdinfo.cddb_dtitle = g_strdup(tmp);
		}
	}	
}


/* do output debug messages */

void dodebug(gint debuglevel, gchar *fmt, ...) {
va_list ap;
gchar tmp[MAXLINE*10];
gchar *p;

	/* output message when debuglevel is high enough */
	if (debuglevel <= debug) {

		/* put together the variable argument list */
		va_start(ap,fmt);
		vsprintf(tmp, fmt, ap);
		va_end(ap);
	
                /* remove first linefeed if any */
		p = index(tmp,'\r');
		if (p != NULL) 
                        *p = ' ';

		fprintf(stderr,"DGB%d: %s",debuglevel,tmp);
	}
}


/* do write to logfile */

void dolog(gint loglevel, gchar *fmt, ...) {
va_list ap;
gchar tmp[MAXLINE*10];
gchar tmp2[MAXLINE];
char timestr[MAXLINE];
time_t acttime;
FILE *lfile;

	/* output message when loglevel is high enough */
	if (loglevel <= setupdata.loglevel && strcmp(setupdata.logfile,"")) {

		/* put together the variable argument list */
		va_start(ap,fmt);
		vsprintf(tmp, fmt, ap);
		va_end(ap);
		
		acttime = time((time_t *) 0);
		strcpy(timestr,ctime(&acttime));

		/* remove last \n from timestr */
		timestr[strlen(timestr)-1] = 0;

		strncpy(tmp2, setupdata.logfile,MAXLINE);
		check_tilde(tmp2);

		lfile = fopen(tmp2,"a");

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

		if (!fprintf(lfile,"%s XCDR %s: %s", timestr,
			XCDROAST_VERSION, tmp)) {
			g_warning("Error appending to logfile\n");
		}
		fclose(lfile);
	}
}


/* notify-beep function */
/* type: 1 = completed task, 2 = warnings */

void dobeep(gint type) {
gint doit;

	doit = 0;

	switch (setupdata.notify_at) {
	case 0: 
		/* we want no beep */
		return;

	case 1:
		/* always */
		doit = 1;
		break;
	case 2:
		/* on completion */
		if (type == 1) 
			doit = 1;
		break;
	case 3:
		/* warnings only */
		if (type == 2)
			doit = 1;
		break;
	default:
		return;
	}

	/* ok..we have to play a sound */
	if (doit == 1) {
		if (setupdata.notify_via == 0) {
			/* dspdevice */
			if (strcmp(setupdata.dsp_device,"") != 0) {
				test_dspdevice_play();	
			}
		} else {
			/* internal speaker */
			gdk_beep();
		}
	}
}


/* does check if the loaded devices are matching the current hardware */
/* returns 0 if ok, 1 if writer/read not match */

gint verify_loaded_config() {
gint i;
gint found1, found2, found3;

	/* found any scsidevices at all? */
	if (scsidevices[0] == NULL) {
		/* now look if this is the condition we got in the config */
		if (setupdata.writer_devnr == -1 &&
		    setupdata.readdev1_devnr == -1 &&	
		    setupdata.readdev2_devnr == -1) {
			/* ok..now devices, its ok */
			return 0;
		}
	}

	i = 0;
	found1 = 0;
	found2 = 0;
	found3 = 0;
	while(scsidevices[i] != NULL) {

		/* check cdwriter */
		if (setupdata.writer_devnr == scsidevices[i]->devnr) {
			if ((strcmp(setupdata.writer_vendor, 
				   scsidevices[i]->vendor) != 0) || 
			    (strcmp(setupdata.writer_model,
				   scsidevices[i]->model) != 0)) {
				/* devices do not match */
				return 1;
			} 
			found1 = 1;
		}

		/* check readdev1 */
		if (setupdata.readdev1_devnr == scsidevices[i]->devnr) {
			if ((strcmp(setupdata.readdev1_vendor, 
				   scsidevices[i]->vendor) != 0) || 
			    (strcmp(setupdata.readdev1_model,
				   scsidevices[i]->model) != 0)) {
				/* devices do not match */
				return 1;
			} 
			found2 = 1;
		}

		/* check readdev2 */
		if (setupdata.readdev2_devnr == scsidevices[i]->devnr) {
			if ((strcmp(setupdata.readdev2_vendor, 
				   scsidevices[i]->vendor) != 0) || 
			    (strcmp(setupdata.readdev2_model,
				   scsidevices[i]->model) != 0)) {
				/* devices do not match */
				return 1;
			} 
			found3 = 1;
		}

		i++;
	}

	if (found1 == 0 || found2 == 0 || found3 == 0) {
		/* devnr did not even match -> missing devices */
		return 1;
	}

	return 0;
}


/* check if the image-dirs fit to our partitions */
/* return 0 if ok, 1 when there were errors (and we edited the list) */

gint verify_loaded_config2 () {
GList *loop, *loop2;
GList *fslist;
gchar dir[MAXLINE];
gchar fs[MAXLINE];
gint free;
gint fsuse;
gint dirsok;

	/* now check if all the loaded image-dir exists and are each on
	   a own partition */

	fslist = NULL;
	dirsok = 0;
	loop = g_list_first(setupdata.image_dirs);
	while (loop) {
		strcpy(dir,(gchar *)loop->data);
		/* get filesystem for this dir */
		free = get_free_space(dir,fs);
		if (free == -1) {
			/* no such directory */
			/* mark to remove this entry from the list...*/
			g_free(loop->data); 
			loop->data = NULL;
			dirsok = 1;
		} else {
			/* check if this dir is already in use */
			/* if not, add to fs-list */
			fsuse = 0;
			loop2 = g_list_first(fslist);
			while (loop2) {
				if (strcmp(fs, (gchar *)loop2->data) == 0) {
					fsuse = 1;
				}
				loop2 = loop2->next;
			}	
			if (fsuse == 0) {
				/* not already used */
				fslist = g_list_append(fslist, g_strdup(fs));
			} else {
				/* remove this entry from list */
				g_free(loop->data); 
				loop->data = NULL;
				dirsok = 1;
			}
		}

		loop = loop->next;
	}

	/* free our temporary list */
	free_glist(&fslist);

	/* now really remove the marked dirs from list */
	loop = g_list_first(setupdata.image_dirs);
	while (loop) {
		loop2 = loop->next;
		if (loop->data == NULL) {
			setupdata.image_dirs = 
				g_list_remove_link(setupdata.image_dirs, loop);
		}
		loop = loop2;
	}

	return dirsok;
}


/* check if this track match the inserted cd (verify tracks) */
/* return 0 if all ok, 1 on some error, 2 if file does not match to cd
   and 3 if we dont want verify audio (checking for readable not necessary 
   because unreadable tracks are not displayed in verify menu */

gint check_vrfy_track(gchar *fname) {
gchar tmp[MAXLINE];

        /* get the discid */
        if (get_discid_from_imagelist(fname,tmp) != 0) {
                /* no discid found in info-file? */
                return 1;
        }

        /* compare with current cd */
        if (strcmp(tmp, cdinfo.cddb_discid) != 0) {
                return 2;
        }

	/* check if its an audio track and we want to verify them */
	if (curset.noaudioverify == 1 && 
		get_type_from_imagelist(fname) == 1) {
		return 3;
	}
        /* all ok */
        return 0;
}


/* build a trackname for image-lists */

void assign_trackname(gchar *titlestr, image_files_t *entry) {

                /* see if there is cd text for this track */
                if (entry->title && entry->artist &&
                    strcmp(entry->title,"") && strcmp(entry->artist,"")) {
                        g_snprintf(titlestr,MAXLINE,"%s / %s",
                                entry->title, entry->artist);
                } else
                if (entry->title && strcmp(entry->title,"")) {
                        strcpy(titlestr, entry->title);
                } else
                if (entry->cddb_ttitle && strcmp(entry->cddb_ttitle,"")) {
                        strcpy(titlestr, entry->cddb_ttitle);
                } else
                if (entry->volname && strcmp(entry->volname,"")) {
                        g_snprintf(titlestr,MAXLINE,"%s / ISO9660",
                                entry->volname);
                }
}


/* check if a filename is on the writelist */

gint is_on_writelist(gchar *file) {
GList *loop;
gchar *track;

        loop = g_list_first(writelist);
        while (loop) {
                track = loop->data;
		if (track && strcmp(track, file) == 0) {
			return 1;
		}
		loop = loop->next;
	}
	return 0;
}


/* free trackreadset */

void clear_trackreadset() {
GList *loop;
track_read_param_t *trackparam;

        loop = g_list_first(trackreadset.trackparams);
        while (loop) {
                trackparam = loop->data;
                g_free(trackparam->trackfile);
                g_free(trackparam);
                loop = loop->next;
        }
	if (trackreadset.trackparams)
        	g_list_free(trackreadset.trackparams);
        trackreadset.trackparams = NULL;
	g_free(trackreadset.tocfile);
	trackreadset.tocfile = g_strdup("");
	g_free(trackreadset.cdtitle);
	trackreadset.cdtitle = g_strdup("");
        trackreadset.nrtracks = 0;
        trackreadset.cdsize = 0;
}


/* transform coordinates when bigfonts are used */

gint tbf(gint koord) {

        if (bigfonts == 1) {
                return (koord * XCDR_TOPLEVEL_X1)/XCDR_TOPLEVEL_X0;
        } else {
                return koord;
        }
}


/* sort a glist */

void sort_glist(GList *filelist) {
GList *first, *last, *list1, *list2;
gchar *str1, *str2, *str3;

        first = g_list_first(filelist);
        last = g_list_last(filelist);
        for (list1 = first; list1 != last; list1 = list1->next) {
                for (list2 = last; list2 != list1; list2 = list2->prev) {
                        str1 = (gchar *) list1->data;
                        str2 = (gchar *) list2->data;

                        if (strcmp (str1, str2) > 0) {
                                str3 = str1;
                                list1->data = list2->data;
                                list2->data = str3;
                        }
                }
        }
}


/* determine path for helper apps */

void get_spawn_path(gchar *app, gchar *ret) {
struct stat buf;

	/* when path is with a leading slash (absolute), do nothing */
	if (app[0] == '/') {
		strcpy(ret,app);
		return;
	}

	/* otherwise its relative - add sharedir first */
	g_snprintf(ret,MAXLINE,"%s/%s", sharedir, app);

	/* now check if this file does exist */
	if (stat(ret,&buf) != 0) {
		/* it does not, so try the fallback */
		g_snprintf(ret,MAXLINE,"%s/%s", prefixdir, app);
	}
	return;
}


/* reroute a command through the wrapper */
/* cmd is one of "CDRECORD", "MKISOFS", "CDDA2WAV", "READCD" */

gchar *get_wrap_path(gchar *cmd, gchar *ret) {
gchar tmp[MAXLINE];

	g_snprintf(tmp,MAXLINE,"%s/%s %s", sharedir, WRAPPER, cmd);
	strncpy(ret, tmp, MAXLINE);

	return ret;
}


/* find a path with enough space to save a mkisofs-image */
/* return 0 if found, 1 on error/disk-full, return 2 if no writeable dir */
/* size is given in kbyte */
/* return via call by reference the size (in kbytes) that will be
   free due overwriting old files. Also return the freed size on
   the directory with the most space available */

gint allocate_master_filename(gint size, gint nr, gchar **return_fname, 
	gint *overwrite, gint *overwritebiggest) {
gchar tmp[MAXLINE];
gchar biggestpath[MAXLINE];
gchar path[MAXLINE];
GList *freedirs;
struct stat buf;
GList *loop, *loop2;
image_dir_free_t *freedir;
gint free,maxfree,tmpkbyte;
gint overwritefree, overwritefreebiggest;
gint ret;

        overwritefree = 0;
        overwritefreebiggest = 0;
        ret = 0;
	freedirs = NULL;
	maxfree = 0;
	strcpy(biggestpath,"");

        /* build image-path/free structure */
        if (curset.image_index == -1) {
                /* automatic setting */
                loop = g_list_first(setupdata.image_dirs);
                while(loop) {
                        strcpy(path,(gchar *)loop->data);

			/* this dir writeable? */
			if (is_dir_writeable(path) == 1) {
				/* no? skip */
				loop = loop->next;
				continue;
			}
                        free = get_free_space(path,NULL);
                        freedir = g_new(image_dir_free_t,1);
                        freedir->path = g_strdup(path);
                        freedir->free = free;
                        freedirs = g_list_append(freedirs,freedir);

                        /* path with biggest available block? */
                        if (free > maxfree) {
                                maxfree = free;
                                strcpy(biggestpath,path);
                        }       
                        loop = loop->next;
                }
		/* no dirs writeable */
		if (freedirs == NULL) {
			*overwrite = 0;
			*overwritebiggest = 0;
			return 2;
		}
        } else {
                /* single path */
                strncpy(path,(gchar *)g_list_nth_data(setupdata.image_dirs,
                        curset.image_index), MAXLINE);

		/* this dir writeable? */
		if (is_dir_writeable(path) == 1) {
			*overwrite = 0;
			*overwritebiggest = 0;
			return 2;
		}
                free = get_free_space(path,NULL);
                freedir = g_new(image_dir_free_t,1);
                freedir->path = g_strdup(path);
                freedir->free = free;
                freedirs = g_list_append(freedirs,freedir);
                maxfree = free;
                strcpy(biggestpath,path);
        }
        /* now we have a structure with all path we are allowed to 
           save data in and how much space is available there */

	strcpy(path,"");

	/* look in which path we have space */
	loop2 = g_list_first(freedirs);
	while (loop2) {
		freedir = loop2->data;

		/* build temporary filename */
		g_snprintf(tmp,MAXLINE, "%s/%s-%02d.%s", freedir->path,
			curset.file_prefix, nr, "img");

		/* already a file with this name on hd? */
		if (stat(tmp,&buf) == 0) {
			/* file exists */
			tmpkbyte = (gint) ((off_t)buf.st_size >> 10);
			overwritefree += tmpkbyte;

			/* file in directory with most space? */
			if (strcmp(freedir->path,biggestpath) == 0) {
				overwritefreebiggest += tmpkbyte;
			}
		} else {
			tmpkbyte = 0;
		}

		/* enough free? consider space that is freed
		   when we overwrite a file (tmpkbyte) */
		if (size < (freedir->free + tmpkbyte)) {
			/* found freespace */
			strcpy(path,freedir->path);
			freedir->free-=size - tmpkbyte;
			break;
		}
		loop2 = loop2->next;
	}

	/* no free space found? */
	if (strcmp(path,"") == 0) {
		ret = 1;
		dodebug(1,"allocate_master_filename: no free space\n");
	} else {
		/* found a file */
		if (return_fname != NULL) {
			g_free(*return_fname);
			*return_fname = g_strdup(tmp);
		}
		dodebug(1,"allocate_master_filename: got %s\n", tmp);
	}

        /* free image-path/free structure */
        loop2 = g_list_first(freedirs);
        while (loop2) {
                freedir = loop2->data;
                g_free(freedir->path);
                g_free(freedir);
                loop2 = loop2->next;
        }
        g_list_free(freedirs);

        *overwrite = overwritefree;
        *overwritebiggest = overwritefreebiggest;

	return ret;
}


/* checks if the current writer does support SANYO burnproof or something
   like that */

gint does_support_burnproof() {
gint count;

	if (drv_options == NULL) return 0;

	count = 0;
	while(drv_options[count] != NULL) {
		if (strcmp(drv_options[count]->driver,"burnfree") == 0) {
			return 1;
		}
		count++;
	}
	return 0;
}


/* do change the group rights for nonroot mode */
/* this seems only be required with /bin/sh being bash2 */

void fix_guid() {

	dodebug(3,"Current gid/egid = %d/%d\n", getgid(), getegid());

	/* if we are not bash2, no special handling */
	if (!runningbash2) 
		return;

	setregid(getegid(), getgid());
	dodebug(3,"Current gid/egid after setregid = %d/%d\n", getgid(), getegid());
}


/* check if a given group-id matches a groupname */

gint match_group_name(gid_t gid, gchar *group) {
struct group *grp;
	
        /* get structure containing name of group */
        grp = getgrgid(gid);

        if (grp && grp->gr_name) {
		dodebug(3,"Matching gid = %d (%s)\n", gid, grp->gr_name);
                if (strncmp(grp->gr_name,group,strlen(group)) == 0) {
                        /* does match */
                        return 1;
                }
        }
        return 0;
}


/* return string with group name */

void return_group_name(gid_t gid, gchar *ret) {
struct group *grp;
	
        /* get structure containing name of group */
        grp = getgrgid(gid);
	
	if (grp && grp->gr_name) {
		strncpy(ret,grp->gr_name,MAXLINE);
		return;
	}
	
	/* unable to get grp name? return id as text */
	g_snprintf(ret,MAXLINE,"%d", (gint) gid);	
	return;	
}


/* return string with username name */

void return_user_name(uid_t uid, gchar *ret) {
struct passwd *pw;
	
	pw = getpwuid(uid);
	
	if (pw && pw->pw_name) {
		strncpy(ret,pw->pw_name,MAXLINE);
		return;
	}
	
	/* unable to get grp name? return id as text */
	g_snprintf(ret,MAXLINE,"%d", (gint) uid);	
	return;	
}


/* return 1 if a group exists (by group name) */

gint check_group_exists(gchar *name) {
struct group *grp;
 
	grp = getgrnam(name);

	if (grp) 
		return 1;
	else
		return 0;
}

/* parse the alternate device string (if any) */

void parse_alt_devs(gchar *str) {
gint i;
gchar *p;
gchar tmp[MAXLINE];

	/* allocate memory */
	alt_scsidevices = g_new0(gchar *,MAXDEVICES);
	i = 0;

	if (str == NULL) {
		/* no devices, return */
		return;
	}

	dodebug(2,"----- list of manually choosen device names -----\n");

	/* get list of devices */
	p = strtok(str,";");
	while (p) {
		strncpy(tmp,p,MAXLINE);
		strip_string(tmp);

		alt_scsidevices[i] = g_strdup(tmp);
		dodebug(2,"alt_device: %d - \"%s\"\n",i, tmp);
	
		p = strtok(NULL,";");
		i++;
		if (i >= MAXDEVICES) {
			g_error("Error: More than %d devices given\n",MAXDEVICES);
		}
	}		

	return;	
}


/* return the path to the chmod command */

gchar *get_chmod_cmd() {
struct stat buf;

	if (stat(CHMOD,&buf) == 0) {
		return CHMOD;
	}
	if (stat(CHMOD2,&buf) == 0) {
		return CHMOD2;
	}
	return NULL;
}


/* return the path to the chgrp command */

gchar *get_chgrp_cmd() {
struct stat buf;

	if (stat(CHGRP,&buf) == 0) {
		return CHGRP;
	}
	if (stat(CHGRP2,&buf) == 0) {
		return CHGRP2;
	}
	return NULL;
}


/* return the path to the chown command */

gchar *get_chown_cmd() {
struct stat buf;

	if (stat(CHOWN,&buf) == 0) {
		return CHOWN;
	}
	if (stat(CHOWN2,&buf) == 0) {
		return CHOWN2;
	}
	return NULL;
}


/* allocate an entry in the nonrootval-list */

void add_to_nonrootvalues(GList **list, gchar *path, gint uid, gint gid, gint mode) {
nonroot_flags_t *entry;

        entry = g_new0(nonroot_flags_t, 1);
        if (entry) {
                entry->path = g_strdup(path);
                entry->uid = (uid_t)uid;
                entry->gid = (gid_t)gid;
                entry->mode = (mode_t)mode;

                *list = g_list_append(*list, entry);
        }
}


/* free the nonrootvalues glist */

void free_nonrootvalues(GList **list) {
GList *loop;
nonroot_flags_t *entry;

	loop = g_list_first(*list);
	while(loop) {
		entry = (nonroot_flags_t *)loop->data;
		g_free(entry->path);
		g_free(entry);

		loop = loop->next;		
	}	
	g_list_free(*list);
	*list = NULL;	
}


/* split the DTITLE line from cddb to artist and title */

void get_artist_and_title_from_cddb(gchar *dtitle, gchar *artist, gchar *title) {
gchar *p;
gint len;

	p = index(dtitle, '/');
	if (p) {
		len = p - dtitle;
		if (len > MAXLINE) 
			len = MAXLINE;
		strncpy(title, dtitle,len);
		title[len] = '\0';	
		strip_string(title);

		strncpy(artist, p+1, MAXLINE);
		strip_string(artist);
	} else {
		strncpy(title, dtitle, MAXLINE);
		strip_string(title);
		strcpy(artist,"");
	}
}


/* switch artist <-> title in a cddb string */

void switch_artist_title(gchar *dtitle) {
gchar title[MAXLINE];
gchar artist[MAXLINE];

	get_artist_and_title_from_cddb(dtitle, title, artist);
	g_snprintf(dtitle, MAXLINE, "%s / %s", title, artist);
}


/* open the xinf-file for the given track and extract artist and title */
/* fallback to cddb if no title given */

void get_title_artist_from_xinf(gchar *file, gchar *artist, gchar *title) {
image_files_t *entry;

	entry = g_new(image_files_t,1);
	entry->path = NULL;
	entry->volname = NULL;
	entry->title = NULL;
	entry->artist = NULL;
	entry->cddb_ttitle = NULL;
	entry->cd_discid = NULL;

	/* open file and extract data */
	get_inf_tracktitle(file, entry);

	if (entry->title && *entry->title) {
		strncpy(title, entry->title, MAXLINE);
	} else {
		/* try cddb title */
		if (entry->cddb_ttitle) {
			strncpy(title, entry->cddb_ttitle, MAXLINE);
		}
	}
	if (entry->artist) {
		strncpy(artist, entry->artist, MAXLINE);
	}

	/* free entry again */
        g_free(entry->path);
        g_free(entry->volname);
        g_free(entry->title);
        g_free(entry->artist);
        g_free(entry->cddb_ttitle);
        g_free(entry->cd_discid);
        g_free(entry);
}


/* returns a file name for the tocfile used in the write-tracks dialog */

void generate_tmp_tocfile_name(gchar *tocfile) {

	g_snprintf(tocfile, MAXLINE, "%s/xcdr-wrtrk-%d.ttoc",
		TMP_XCDR_DIR, (gint) getpid());
}
 

/* returns a unique filenames that is not in use yet */
/* or empty string on error */

void generate_tmp_file_name(gchar *ext, gchar *file1) {
gchar randchars[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
gint i, done, length, count, ind;
gchar tmp[MAXLINE];
gchar fname[MAXLINE];
struct stat buf;

	done = 0;
	count = 0;
	length = strlen(randchars);

	/* try 10 times to get a unique filename..then give up */
	while (count < 10) {
		/* gen random string */
		for (i = 0; i < 4; i++) {
                        ind = (gint)((gfloat)length*rand()/(RAND_MAX+1.0)); 
                        tmp[i] = randchars[ind];
                }
		tmp[4] = '\0';

		g_snprintf(fname, MAXLINE, "%s/xcdr%s.%s", TMP_XCDR_DIR,
			tmp,ext);

		/* already such a file on disk? */
		if (stat(fname,&buf)) {
			/* no its not - good for us! */
			done = 1;
			break;
		}
		count++;
	}

	if (done == 0) {
		strcpy(file1,"");
	} else {
		strcpy(file1,fname);
	}
}


/* create a 0 byte file */

gint write_empty_file(gchar *fname) {
FILE *fd;

	fd = fopen(fname,"w"); 
	if (!fd) return 1;

	dodebug(2,"creating temporary file %s\n", fname);

	fclose(fd);
	return 0;
}


/* check if our writer is from sony..needed for some multisession checks */

gint is_a_sony(gint devnr) {
gchar tmp[MAXLINE];

	strcpy(tmp,"");
	convert_devnr2vendor(devnr, tmp);

	if (strncasecmp(tmp,"SONY",4) == 0) {
		return 1;
	}

	return 0;	
}


/* parse a version id like 1.11a23 into the structure (1.11 is also ok) */
/* returns 1 on failure */

gint parse_version_str(gchar *str, version_id_t *ver) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar *p;
gint i, found;
	strncpy(tmp, str, MAXLINE);
	p = strtok(tmp,".");
	if (p) {
		ver->major = atoi(p);
	} else {
		return 1;
	}
	p = strtok(NULL,"");
	if (p) {
		/* look for letter */
		found = -1;
		strncpy(tmp2,p, MAXLINE);
		for (i = 0; i < strlen(tmp2); i++) {
			if (!isdigit((gint)tmp2[i])) {
				ver->devel = tolower(tmp2[i]);
				found = i;
				break;
			}
		}
		if (found == -1) {
			/* no letter? full version then? */
			ver->minor = atoi(tmp2);
			ver->patch = 0;
			ver->devel = 'h';	/* highest devel char */
		} else {
			strncpy(tmp,tmp2,found);
			tmp[found] = '\0';
			ver->minor = atoi(tmp);
			
			strncpy(tmp,tmp2+found+1, MAXLINE);
			ver->patch = atoi(tmp);
		}
	} else {
		return 1;
	}

	return 0;
}


/* compares two version strings. Return -1 when older, 0 when equal 
   and 1 when newer */

gint compare_versions(gchar *gotversion, gchar *minimal_version) {
version_id_t ver0, ver1;
gint bigver0, bigver1;
 
	/* convert version strings into compareable values */
	if (parse_version_str(gotversion, &ver0)) 
		return -1;
	if (parse_version_str(minimal_version, &ver1)) 
		return -1;

	/* calculate a big pure numeric version str */
	bigver0 = ver0.major * 100000 + ver0.minor * 1000 + 
		  (ver0.devel - 'a') * 100 + ver0.patch;
	bigver1 = ver1.major * 100000 + ver1.minor * 1000 + 
		  (ver1.devel - 'a') * 100 + ver1.patch;

	if (bigver1 == bigver0) return 0;
	if (bigver1 < bigver0) return 1;

	return -1;
}


/* searches a list of directories for the biggest common path
   compontent  e.g. /home/tn/bla and /home/tn/src  would result in
   -> /home/tn */

void get_common_path_component(GList *dirs, gchar *common) {
GList *loop;
gchar tmp[MAXLINE];
gchar match[MAXLINE];
gint i,len;
gchar *p;

	loop = g_list_first(dirs);
	/* init match str */
	strncpy(match, loop->data, MAXLINE);
	while (loop) {
		strncpy(tmp, loop->data, MAXLINE);

		/* which str is shorter? */
		if (strlen(tmp) > strlen(match)) {
			len = strlen(match);
		} else {
			len = strlen(tmp);
		}
		for (i = 0; i < len; i++) {
			/* search until mismatch */
			if (tmp[i] != match[i]) {
				break;
			}
		}
		/* found shortest common path */
		match[i] = '\0';

		/* now match again */
		loop = loop->next;
	}

	/* match contains now the longest common paths..remove now
	   any non directory parts at the end */
	p = rindex(match,'/');
	if (p) {
		*p = '\0';	
	}
	strncpy(common, match, MAXLINE);
}

