/*
 *	xtools.c
 *	27.3.99 tn
 *
 *
 *  Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
 *
 *  This file is part of xcdroast.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

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

#include "largefile.h"

#include <locale.h>
#include "gettext.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>

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

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

extern gint debug;
extern writerreader_devices_t **writerreaderdevs;
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 gchar sharedir[MAXLINE];
extern gchar prefixdir[MAXLINE];
extern gint c_locale_is_utf8;

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

	/* tooltips wanted? */
	if (setupdata.option_tooltips == 0) {
		return;
	}

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


/*
 * 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) {
GdkColor coldef;
PangoFontDescription *fontdef;

	if (color) {
		gdk_color_parse (color, &coldef);
		gtk_widget_modify_fg (widget, GTK_STATE_NORMAL, &coldef);
 	}	
	if (font) {
		fontdef = pango_font_description_from_string(font);
        	gtk_widget_modify_font(widget, fontdef);
	}
}


/*
 * set font and color for a frame
 */
void set_font_and_color_frame(GtkWidget *widget, gchar *font, gchar *color) {

	set_font_and_color(gtk_frame_get_label_widget(GTK_FRAME(widget)), font, color);
}


/*
 * sets the enable status for a label widget
 */
void set_labelcolor(GtkWidget *widget, gchar *color) {

	if (strcmp(color, DISABLEDCOLOR) == 0)
		gtk_widget_set_sensitive(widget, FALSE);
	else
		gtk_widget_set_sensitive(widget, TRUE);
}


/*
 * sets a certain row in a clist to a font
 */
void set_clist_row_font(GtkCList *clist, gint row, gchar *ffont) {
GtkStyle *style;

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

	if (ffont && strcmp(ffont, PANGO_ITALIC) == 0) {
		pango_font_description_set_style(style->font_desc,
			PANGO_STYLE_ITALIC);
	}
	if (ffont && strcmp(ffont, PANGO_BOLD) == 0) {
		pango_font_description_set_weight(style->font_desc, 
			PANGO_WEIGHT_BOLD);
	}
	
	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("Can't 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 found;
guint i;

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

        if (strlen(in) == 0) {
                strcpy(out,"");
                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);

	/* internally we use not utf8, convert back from widget */
	convert_for_gtk2_filename(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(writerreaderdevs[i] != NULL) {
                if (devnr == writerreaderdevs[i]->devnr) {
                        writerreaderdevs[i]->sector_size = size;
                        return;
                }
                i++;
        }
}


/*
 * get scsi-sector-size for a given device
 */
gint get_sectorsize(gint devnr) {
gint i;

        i = 0;
        while(writerreaderdevs[i] != NULL) {
                if (devnr == writerreaderdevs[i]->devnr) {
                        return(writerreaderdevs[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(writerreaderdevs[i] != NULL) {
		if (devnr == writerreaderdevs[i]->devnr) {
			if (!writerreaderdevs[i]->devicestr) {
				g_error("empty device string?");
			}
			g_snprintf(tmp,MAXLINE,"%s %s [%s]",
				writerreaderdevs[i]->vendor, 
				writerreaderdevs[i]->model,
				writerreaderdevs[i]->devicestr);
			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(writerreaderdevs[i] != NULL) {
		if (devnr == writerreaderdevs[i]->devnr) {
			strcpy(str,writerreaderdevs[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(writerreaderdevs[i] != NULL) {
		if (devnr == writerreaderdevs[i]->devnr) {
			strcpy(str,writerreaderdevs[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(writerreaderdevs[i] != NULL) {
		if (devnr == writerreaderdevs[i]->devnr) {
			if (!writerreaderdevs[i]->devicestr) {
				g_error("empty device string?");
			}
			strncpy(str,
			    writerreaderdevs[i]->devicestr,
			    MAXLINE);
			convert_escape(str);
			return 0;
		}
		i++;
	}
	strcpy(str,"");
	return 1;
}


/*
 * save as convert_devnr2busid(), but return with dev= component
 */
gint convert_devnr2busid_dev(gint devnr, gchar *str) {
gint i;
gchar tmp[MAXLINE];

	i = 0;
	while(writerreaderdevs[i] != NULL) {
		if (devnr == writerreaderdevs[i]->devnr) {
			if (!writerreaderdevs[i]->devicestr) {
				g_error("empty device string?");
			}
			strncpy(tmp,
			    writerreaderdevs[i]->devicestr,
			    MAXLINE);
			convert_escape(tmp);
			g_snprintf(str,MAXLINE,"dev= \"%s\"",tmp);
			return 0;
		}
		i++;
	}
	strcpy(str,"");
	return 1;
}


/*
 * Convert kbytes to MiB/min or MiB/h or GiB/h string.
 * Displays MiB or GiB when using 2048b sectors.
 * Displays minutes or hours suitable to audio tracks on the hard drive.
 * This is not correct when displaying the minute-size of DATA tracks!
 */ 
void convert_kbytes2mbminstring(gint kbytes, gchar *str) {
gint MiB, GiB;
gint min, sec, frms;
gint hrs, min1;
gint frames;
gint64 tmpsize;

	MiB = kbytes/1024;
	GiB = kbytes/1048576;
	tmpsize = (gint64)kbytes * 1024;
	frames = (gint) ((gint64)tmpsize/CDDAFRAME);
	
	hrs = frames/(3600*75);
	min1 = (frames%(3600*75))/60/75;
	min = frames/(60*75);
	sec = (frames%(60*75))/75;
	frms = (frames%75);

	if (min < 600) {
		g_snprintf(str,MAXLINE,"%d MiB / %d:%02d.%02d min",MiB,min,sec,frms);
	} else if (MiB < 10240) {
		g_snprintf(str,MAXLINE,"%d MiB / %d:%02d h",MiB,hrs,min1);
	} else {
		g_snprintf(str,MAXLINE,"%d GiB / %d:%02d h",GiB,hrs,min1);
	}
}


/*
 * convert kbytes to MiB/min string
 * displays MiB 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 MiB;
gint min;
gint sec;
gint frames;
gint frms;

	MiB = kbytes/1024;
	frames = kbytes/2;	
	min = frames/(60*75);
	sec = (frames%(60*75))/75;
	frms = (frames%75);

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


/*
 * convert frames to MiB/min string
 * should only be used for audio or full disk info
 */
void convert_frames2mbminstring(gint frames, gchar *str) {
gint MiB;
gint min;
gint sec;
gint frms;
gint64 tmpsize;

	tmpsize = (gint64)frames * CDDAFRAME;
	MiB = (gint) ((gint64)tmpsize >> 20);

	min = frames/(60*75);
	sec = (frames%(60*75))/75;
	frms = (frames%75);

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


/*
 * convert sectors to MiB/min string
 * should only be used for data or full disk info
 */
void convert_sectors2mbminstring(gint sectors, gchar *str) {
gint MiB;
gint min;
gint sec;
gint frms, frames;
gint64 tmpsize;

	tmpsize = (gint64)sectors * DATASECTORSIZE;
	MiB = (gint) ((gint64)tmpsize >> 20);

	frames = (gint) (tmpsize / CDDAFRAME);
	min = frames/(60*75);
	sec = (frames%(60*75))/75;
	frms = (frames%75);

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


/*
 * convert frames/sectors to MiB string
 * note - this is only true for DATA-tracks
 */
void convert_frames2mbstring(gint frames, gchar *str) {
gint MiB;

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


/*
 * convert kbytes to MiB string
 */
void convert_kbytes2mbstring(gint kbytes, gchar *str) {
gint MiB;

	MiB = kbytes/1024;
	g_snprintf(str,MAXLINE,"%d MiB",MiB);
}


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

	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
 */
static 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 too short filenames */
			if (len2 < len) continue;

			if (strncasecmp((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, %"LL_FORMAT", %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));
}


/*
 * Fills a string saying what type of disc we are currently handling
 * and also returns a corresponding number for each type of disc.
 * mode = 0: checks the disc in the drive
 * mode = 1: checks trackreadset for writing
 */
gint determine_disc_type(gchar *type_name, gint mode) {
gint i;
gint audio,data;
gint type_num;
gchar tmp[MAXLINE];
GList *loop;
track_read_param_t *trackparam;
	
	/* unknown type */
	type_num = -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_num = 0;
	} else
	/* pure audio-cd */
	if (data == 0 && audio > 0) {
		type_num = 1;
	} else
	/* mixed-mode */
	if (mode == 0 && data == 1 && audio > 0 && trackinfo[0]->type == 0) {
		type_num = 2;
	} else
	if (mode == 1 && data == 1 && audio > 0 && trackparam->tracktype == 0) {
		type_num = 2;
	} else
	/* cd-extra */
	if (mode == 0 && data == 1 && audio > 0 && cdinfo.have_cdextra) {
		type_num = 3;
	} else
	if (mode == 1 && data == 1 && audio > 0 && trackparam->tracktype == 1) {
		/* one data, at least one audio and first track audio */
		type_num = 3;
	} else 
	/* multisession */
	if (data > 1 && audio == 0) {
		type_num = 4;
	} 

	/* enough for now */
	switch (type_num) {
	case 0:
		if ((mode == 0 && cdinfo.total_size < 450000) ||
		    (mode == 1 && trackreadset.cdsize < 450000)) {
			/* 450000 equals 100:00.00 min */
			strncpy(tmp,_("Data-CD"),MAXLINE);
		} else {
			strncpy(tmp,_("Data-DVD"),MAXLINE);
		}
		break;
	case 1:
		strncpy(tmp,_("Audio-CD"),MAXLINE);
		break;
	case 2:
		strncpy(tmp,_("Mixed-Mode-CD"),MAXLINE);
		break;
	case 3:
		strncpy(tmp,_("CD-Extra"),MAXLINE);
		break;
	case 4:
		strncpy(tmp,_("Multisession-CD"),MAXLINE);
		break;
	default:
		strncpy(tmp,_("Unknown"),MAXLINE);
		break;
	}

	/* return value(s) */
	strncpy(type_name,tmp,MAXLINE);
	return (type_num);
}


/*
 * 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) {
			strncpy(tmp,(gchar *)loop->data,MAXLINE);

			/* 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 disk space and the image-directory-settings.
 * return 0 if ok, 1 on error/disk full, 2 if no writeable dir found
 * and 3 if we are about to overwrite a link,
 * 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) {
			strncpy(path,(gchar *)loop->data,MAXLINE);

			/* 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;
				strncpy(biggestpath,path,MAXLINE);
			}	
			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;
		strncpy(biggestpath,path,MAXLINE);
	}
	/* now we have a structure with all paths 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,"iso");
		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) {

				/* is a link? */
				if (check_islink(tmp, NULL)) {
					g_warning("Possibly overwriting a link at %s - not allowed.\n", tmp);
					return 3;
				}

				/* file exists */
				tmpkbyte = (gint) ((off_t)buf.st_size >> 10);
				if (tmpkbyte == 0) {
					/* file smaller than one kb? */
					/* assume 1 kb then */
					tmpkbyte = 1;
				}
				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 */
				strncpy(path,freedir->path,MAXLINE);
				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(1000);
}


/*
 * 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,
 * 4 when audio files with wrong isrc or mcn found
 */ 
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, invalidisrcmcn;

	sumframes = 0;
	errsize = 0;
	invalidisrcmcn = 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 */

			/* check if ISRC/MCN info is valid */
			invalidisrcmcn += check_valid_isrc_mcn(trackparam->trackfile);

			size = (off_t) ((off_t)trackparam->frames * CDDAFRAME);
			sumframes += trackparam->frames;
		}

		/*
		 * check size of file - allow an offset of 4096 bytes
		 * and an offset of 152*2048 (leadout+runout sectors)
		 * (and allow an 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 (invalidisrcmcn > 0) {
		return 4;
	}

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


/*
 * correct any problem in an .inf file with an invalid ISRC or MCN number.
 * Return 1 if there was a problem. (like permission denied)
 */
gint clear_isrc_mcn_from_tracks() {
GList *loop;
track_read_param_t *trackparam;
gint stat;

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

		if (check_valid_isrc_mcn(trackparam->trackfile)) {
			/* ok, thats one of the bad files */
			stat += clear_isrc_mcn_from_inffile(trackparam->trackfile);
		}

		loop = loop->next;
	}

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


/*
 * 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) {
gint fd;

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

	if (!is_std_wav_file(fd, NULL)) {
		/* no wav at all or not cd-quality */
		close(fd);
		return 0;
	}

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


/*
 * small thing for iso-check
 */
static 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];
GtkWidget *tmpdialog;

	/*
	 * create dialog, just to grab the focus on it
	 * and make xcdroast no longer "clickable"
	 */
	tmpdialog = my_gtk_dialog_new();
        gtk_grab_add(tmpdialog);

	get_cd_toc(devnr);

	gtk_grab_remove(GTK_WIDGET(tmpdialog));
	gtk_widget_destroy(tmpdialog);
	
	/* no disc 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 disc title to iso9660-volname because
	   that's 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 || strcmp(cdinfo.cddb_dtitle, tmp) != 0) { 
			/* no? then use iso-header as title */
			cdinfo.title = NULL;
			cdinfo.cddb_dtitle = g_strdup(tmp);
		}
	}	
}


/*
 * do output debug messages
 */
void dodebug(gint debuglevel, gchar *fmt, ...) {
va_list ap;
gchar tmp[MAXLINE*21];
gchar *p;
guint i;

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

		/* remove \b if any */
		for (i = 0; i < strlen(tmp); i++) {
			if (tmp[i] == '\b') {
				tmp[i] = ' ';
			}
		}

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


/*
 * do write to logfile
 */
void dolog(gint loglevel, gchar *fmt, ...) {
va_list ap;
gchar tmp[MAXLINE*21];	/* two buffers up to 10k plus saveguard */
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);
		strncpy(timestr,ctime(&acttime),MAXLINE);

		/* 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:
 * no beep, always, on completion, warnings only
 */
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();
		}
	}
}


/*
 * 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_config () {
GList *loop, *loop2;
GList *fslist;
gchar dir[MAXLINE];
gchar fs[MAXLINE];
gint free;
gint fsuse;
gint dirsok;

	/*
	 * now check if all the loaded image-dirs exist
	 * and are each on an own partition
	 */
	fslist = NULL;
	dirsok = 0;
	loop = g_list_first(setupdata.image_dirs);
	while (loop) {
		strncpy(dir,(gchar *)loop->data,MAXLINE);
		/* 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 matches the inserted disc (verify tracks)
 * return 0 if all ok, 1 on some error, 2 if file does not match to disc
 * and 3 if we don't 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 disc */
        if (strcmp(tmp, cdinfo.cddb_discid) != 0) {
                return 2;
        }

	/* check if it's 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("");
	g_free(trackreadset.cd_discid);
	trackreadset.cd_discid = 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 of strings
 */
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;
                        }
                }
        }
}


/*
 * sort a glist of integers
 */
void sort_int_glist(GList *intlist) {
GList *first, *last, *list1, *list2;
gint int1, int2, int3;

        first = g_list_first(intlist);
        last = g_list_last(intlist);
        for (list1 = first; list1 != last; list1 = list1->next) {
                for (list2 = last; list2 != list1; list2 = list2->prev) {
                        int1 = GPOINTER_TO_INT(list1->data);
                        int2 = GPOINTER_TO_INT(list2->data);

                        if (int1 > int2) {
                                int3 = int1;
				list1->data = list2->data;
				list2->data = GINT_TO_POINTER(int3);
                        }
                }
        }
}


/*
 * 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 it's 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";
 * if the cdrtools use localization then the prefix LANG=en is needed
 */
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;
}


/*
 * returns the gracetime to use:
 * set to 3 seconds in order to prevent the volume management
 * from interrupting the write process
 */
gint get_gracetime() {
	return 3;
}


/*
 * 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
 * return 3 if we would overwrite a link -> possible exploitable
 * 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) {
                        strncpy(path,(gchar *)loop->data, MAXLINE);

			/* 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;
                                strncpy(biggestpath,path,MAXLINE);
                        }       
                        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;
                strncpy(biggestpath,path,MAXLINE);
        }
        /* now we have a structure with all paths 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, "iso");

		/* already a file with this name on hd? */
		if (stat(tmp,&buf) == 0) {

			/* is a link? */
			if (check_islink(tmp, NULL)) {
				g_warning("Possibly overwriting a link at %s - not allowed.\n", tmp);
				return 3;
			}

			/* file exists */
			tmpkbyte = (gint) ((off_t)buf.st_size >> 10);
			if (tmpkbyte == 0) {
				/* file smaller than one kb? */
				/* assume 1 kb then */
				tmpkbyte = 1;
			}
			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 */
			strncpy(path,freedir->path,MAXLINE);
			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 supports
 * SANYO burnproof or something like that
 */
gint does_support_burnproof(gint devnr) {
gint i;
gchar *flags;

        i = get_writerreaderdevs_index(devnr);
        if (i == -1)
                return 0;

        flags = writerreaderdevs[i]->writer_flags;
        if (!flags)     
                return 0;

	if (strstr(flags,"BURNFREE"))
		return 1;

	return 0;
}

/*
 * checks if the current writer supports
 * Plextor VariRec
 */
gint does_support_varirec(gint devnr) {
gint i;
gchar *flags;

        i = get_writerreaderdevs_index(devnr);
        if (i == -1)
                return 0;

        flags = writerreaderdevs[i]->writer_flags;
        if (!flags)     
                return 0;

	if (strstr(flags,"VARIREC")) 
		return 1;

	return 0;
}

/*
 * checks if the current writer supports
 * Yamaha audiomaster
 */
gint does_support_audiomaster(gint devnr) {
gint i;
gchar *flags;

        i = get_writerreaderdevs_index(devnr);
        if (i == -1)
                return 0;

        flags = writerreaderdevs[i]->writer_flags;
        if (!flags)     
                return 0;

	if (strstr(flags,"AUDIOMASTER"))
		return 1;

	return 0;
}

/*
 * checks if the current writer supports
 * forcespeed
 */
gint does_support_forcespeed(gint devnr) {
gint i;
gchar *flags;

        i = get_writerreaderdevs_index(devnr);
        if (i == -1)
                return 0;

        flags = writerreaderdevs[i]->writer_flags;
        if (!flags)     
                return 0;

	if (strstr(flags,"FORCESPEED"))
		return 1;

	return 0;
}


/*
 * 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 chosen 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
 */
void get_chmod_cmd(gchar *cmd) {
struct stat buf;
gchar tmp[MAXLINE], tmp2[MAXLINE];
gchar *p1;

	/* if path is set absolute don't search for it */
	strncpy(tmp, CHMOD, MAXLINE);
	if (tmp[0] == '/') {
		if (stat(CHMOD,&buf) == 0) {
			strncpy(cmd, CHMOD, MAXLINE);
		} else {
			strcpy(cmd,"");
		}
		return;
	}

	strncpy(tmp,CHOWNGRPMOD_PATH,MAXLINE);

	/* loop through path and try each one */
	p1 = strtok(tmp,":");
	while (p1) {
		g_snprintf(tmp2,MAXLINE,"%s/%s", p1, CHMOD);
		if (stat(tmp2,&buf) == 0) {
			strncpy(cmd, tmp2, MAXLINE);
			return;
		}
		p1 = strtok(NULL,":");
	}

	/* not found */
	strcpy(cmd,"");
	return;
}


/*
 * return the path to the chgrp command
 */
void get_chgrp_cmd(gchar *cmd) {
struct stat buf;
gchar tmp[MAXLINE], tmp2[MAXLINE];
gchar *p1;

	/* if path is set absolute don't search for it */
	strncpy(tmp, CHGRP, MAXLINE);
	if (tmp[0] == '/') {
		if (stat(CHGRP,&buf) == 0) {
			strncpy(cmd, CHGRP, MAXLINE);
		} else {
			strcpy(cmd,"");
		}
		return;
	}

	strncpy(tmp,CHOWNGRPMOD_PATH,MAXLINE);

	/* loop through path and try each one */
	p1 = strtok(tmp,":");
	while (p1) {
		g_snprintf(tmp2,MAXLINE,"%s/%s", p1, CHGRP);
		if (stat(tmp2,&buf) == 0) {
			strncpy(cmd, tmp2, MAXLINE);
			return;
		}
		p1 = strtok(NULL,":");
	}

	/* not found */
	strcpy(cmd,"");
	return;
}


/*
 * return the path to the chown command
 */
void get_chown_cmd(gchar *cmd) {
struct stat buf;
gchar tmp[MAXLINE], tmp2[MAXLINE];
gchar *p1;

	/* if path is set absolute don't search for it */
	strncpy(tmp, CHOWN, MAXLINE);
	if (tmp[0] == '/') {
		if (stat(CHOWN,&buf) == 0) {
			strncpy(cmd, CHOWN, MAXLINE);
		} else {
			strcpy(cmd,"");
		}
		return;
	}

	strncpy(tmp,CHOWNGRPMOD_PATH,MAXLINE);

	/* loop through path and try each one */
	p1 = strtok(tmp,":");
	while (p1) {
		g_snprintf(tmp2,MAXLINE,"%s/%s", p1, CHOWN);
		if (stat(tmp2,&buf) == 0) {
			strncpy(cmd, tmp2, MAXLINE);
			return;
		}
		p1 = strtok(NULL,":");
	}

	/* not found */
	strcpy(cmd,"");
	return;
}


/*
 * 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 prefix used in the copy audio on-the-fly dialog
 */
void generate_tmp_prefix_name(gchar *tmpprefix) {

	g_snprintf(tmpprefix, MAXLINE, "%s/xcdr-tmp-%d",
		TMP_XCDR_DIR, (gint) getpid());
}
 

/*
 * returns a unique filename that is not in use yet
 * or an 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 the hard disk? */
		if (stat(fname,&buf)) {
			/* no, there isn't - good for us! */
			done = 1;
			break;
		}
		count++;
	}

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


/*
 * 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 *p,*q,*r;

        ver->major = ver->minor = ver->patch = ver->branch = 0;
	ver->devel='h';

	strncpy(tmp, str, MAXLINE);
	p = tmp;
	for(p=tmp; (*p != 0) && (!isdigit((int) *p ) );p++); /* scan for first digit */
	if(*p)
        {	
          q = strstr(p,".");
          if(q) /* then there's a minor version number */
          {
            *q++ = 0;
            r = strstr(q,".");
            if(r) /* then there's a second dot - call it branch number */
            {
              *r++ = 0;
              ver->branch = atoi(r);
            }
            ver->minor = atoi(q);
            if(r)q=r; /* skip to after branch number if there was one */
          }
	  ver->major = atoi(p);
	  if(q)p=q; /* skip to after minor (and maybe branch) */
	  for( q=p; (*q != 0) && (isdigit((int) *q ));q++);
	  if(*q) 
	    ver->devel = tolower(*q++);
	  else 
	    q=p;
	  if(*q)
	  {
            if(isdigit((int) *q)) ver->patch = atoi(q);
	  }
	  return 0;
	}
	return 1;
}


/*
 * 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;
unsigned long 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 * 10000000 + ver0.minor * 100000 + 
	          ver0.branch * 1000 +
		  (ver0.devel - 'a') * 100 + ver0.patch;
	bigver1 = ver1.major * 10000000 + ver1.minor * 100000 + 
	          ver1.branch * 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);
}


/*
 * return driveropts string for cdrecord
 * return 1 if varirec is enabled
 */
gint do_driveropts(gchar *out, gint devnr) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gint varirecon;

	varirecon = 0;
	strcpy(tmp,"");
	if (does_support_burnproof(devnr)) {
		if (curset.writeburnfree) {
			strcat(tmp,"burnfree");
		} else {
			strcat(tmp,"noburnfree");
		}
	}
	if (does_support_audiomaster(devnr)) {
		if (curset.writeaudiomaster) {
			if (tmp[0] != '\0') 
				strcat(tmp,",");
			strcat(tmp,"audiomaster");
		}
	}
	if (does_support_forcespeed(devnr)) {
		if (tmp[0] != '\0') 
			strcat(tmp,",");
		if (curset.writeforcespeed) {
			strcat(tmp,"forcespeed");
		} else {
			strcat(tmp,"noforcespeed");
		}
	}
	if (does_support_varirec(devnr)) {
		if (curset.writevarirec < 50) {
			if (tmp[0] != '\0') 
				strcat(tmp,",");
			g_snprintf(tmp2,MAXLINE,"varirec=%d", curset.writevarirec);
			strcat(tmp,tmp2);
			varirecon = 1;
		}
	}
	
	if (tmp[0] != '\0') {
		/* added some options? */
		strcpy(tmp2, "driveropts=");
		strcat(tmp2, tmp);
	} else {
		strcpy(tmp2,"");
	}
	strncpy(out, tmp2, MAXLINE);

	return varirecon;
}


/*
 * return index in writerreaderdevs structure for given devnr
 * -1 when not found
 */
gint get_writerreaderdevs_index(gint devnr) {
gint i, found;

        i = 0;
	found = 0;
        while(writerreaderdevs[i] != NULL) {
                if (writerreaderdevs[i]->devnr == devnr) {
			found = 1;
                        break;
                }
                i++;
        }

	if (found) {
		return i;
	} else {
		return -1;
	}
}


/*
 * return 1 if the device devnr supports the given write mode
 */
gint writemode_supported(gint mode, gint devnr) {
gint i;
gchar *modes;

	i = get_writerreaderdevs_index(devnr);
	if (i == -1)
		return 1;

	modes = writerreaderdevs[i]->writer_modes;
	if (!modes) 	
		return 1;

	/* no modes? allow all in this case */
	if (strlen(modes) == 0) 
		return 1;

	switch(mode) {
		
		case 0: 
			if (strstr(modes, "DAO"))
				return 1;
			/* SAO in the middle of the mode string? */
			if (strstr(modes, "SAO "))
				return 1;
			/* SAO at the end of the string? */
			if (strlen(modes) >= 3 && 
			    strcmp((modes + strlen(modes) - 3), "SAO") == 0) 
				return 1;
			break;
		case 1:
		case 2:
			if (strstr(modes, "TAO"))
				return 1;
			break;	
		case 3:
			if (strstr(modes,"RAW/R96R"))
				return 1;
			break;
		case 4:
			if (strstr(modes,"RAW/R96P"))
				return 1;
			break;
		case 5:
			if (strstr(modes,"RAW/R16"))
				return 1;
			break;
		default:
			return 0;
	}
	return 0;
}


/*
 * free all of the current writerreader structure and reset all
 */
void free_writerreader_data() {
gint i;

        i = 0;
        while(writerreaderdevs[i] != NULL) {
		/* free first string data */
		g_free(writerreaderdevs[i]->devicestr);
		g_free(writerreaderdevs[i]->writer_flags);
		g_free(writerreaderdevs[i]->writer_modes);

		g_free(writerreaderdevs[i]);

		i++;
        }

	g_free(writerreaderdevs);
	writerreaderdevs = NULL;

	/* set devicenumbers back to default -1 */
	setupdata.writer_devnr = -1;
	setupdata.reader_devnr = -1;
	curset.writer_devnr = -1;
	curset.reader_devnr = -1;

	return;
}


/*
 * remove a device from the structure
 */
void remove_from_writerreader_data(gint devnr) {
gint startindex, endindex;
gint i;

	startindex = -1;
	i = 0; 
	while(writerreaderdevs[i] != NULL) {
		if (writerreaderdevs[i]->devnr == devnr) {
			startindex = i;
		}
		i++;
	}
	endindex = i - 1;

	if (startindex == -1 || endindex == -1) {
		/* nothing to do */
		return;
	} 

	/* erase entry from memory */
	g_free(writerreaderdevs[startindex]->devicestr);
	g_free(writerreaderdevs[startindex]->writer_flags);
	g_free(writerreaderdevs[startindex]->writer_modes);
	g_free(writerreaderdevs[startindex]);

	/* shift data to fill empty place */
	for (i = startindex; i < endindex; i++) {
		writerreaderdevs[i] = writerreaderdevs[i+1];
	}

	/* last entry is now zero */
	writerreaderdevs[endindex] = NULL;

	/* set devicenumbers back to default -1 if required */
	if (setupdata.writer_devnr == devnr) 
		setupdata.writer_devnr = -1;
	if (setupdata.reader_devnr == devnr) 
		setupdata.reader_devnr = -1;
	if (curset.writer_devnr == devnr) 
		curset.writer_devnr = -1;
	if (curset.reader_devnr == devnr) 
		curset.reader_devnr = -1;
}


/*
 * returns the last used index - or -1 if no entries at all
 */
gint get_last_writerreaderdevs_index() {
gint count;

	count = 0;
        while(writerreaderdevs[count] != NULL) {
                if (count == MAXDEVICES-1) {
			g_error("To many devices");
                }
                count++;
        }
        count--;

	return count;
}


/*
 * saves the size of the given window in the burnwindow-geometry
 */
void store_win_geometry(GtkWidget *win) {

        if (setupdata.option_savepos) {
                gdk_window_get_root_origin(win->window,
                        &setupdata.burnwindow.x, 
                        &setupdata.burnwindow.y);
                setupdata.burnwindow.width = gdk_window_get_width(win->window);
                setupdata.burnwindow.height = gdk_window_get_height(win->window);
        } 
}


/*
 * set the size of a given window from burnwindow-geometry
 */
gint set_win_geometry(GtkWidget *win) {
gint result;

	result = 0;
        if (setupdata.option_savepos && 
             setupdata.burnwindow.width != -1 && 
             setupdata.burnwindow.height != -1 &&
             setupdata.burnwindow.x != -1 &&
             setupdata.burnwindow.y != -1) {

		gtk_widget_realize(win);
                gdk_window_move_resize(win->window,
                        setupdata.burnwindow.x,
                        setupdata.burnwindow.y,
                        setupdata.burnwindow.width,
                        setupdata.burnwindow.height);
		gtk_window_move(GTK_WINDOW(win), 
			setupdata.burnwindow.x,
			setupdata.burnwindow.y);
		result = 1;
        }

	return result;
}


/*
 * set or reset the xcdroast title bar for both toplevel and one dialog
 * window - adds a percent counter if requested
 */
void set_xcdr_title(GtkWidget *dialog, GtkWidget *dialog2, gint val) {
char tmp[MAXLINE];

	if (val >= 0 && val <= 100 && setupdata.option_titleprogress) {
		g_snprintf(tmp, MAXLINE, "%d%% %s %s", val, 
			T_XCDROAST, XCDROAST_VERSION);
	} else {
		g_snprintf(tmp, MAXLINE, "%s %s", 
			T_XCDROAST, XCDROAST_VERSION);
	}	

	if (dialog) 
		gtk_window_set_title(GTK_WINDOW(dialog), tmp);
	if (dialog2) 
		gtk_window_set_title(GTK_WINDOW(dialog2), tmp);
}


/*
 * returns the devnr of a given devicestr or -1 when not found
 */
gint get_writerreaderdevs_index_from_devstr(gchar *devstr) {
gint count;

	count = 0;
        while(writerreaderdevs[count] != NULL) {
		if (strncmp(writerreaderdevs[count]->devicestr, devstr, MAXLINE)== 0) {
			return count;
		}
                count++;
        }

	return -1;
}


/*
 * return whether we have an empty CD-R or DVD-R in the drive
 * we are sure that there IS a medium there - but we don't know which type
 * check only if device is dvdwriter and we have prodvd installed
 */
gchar *return_media_type(gint devnr) {
gchar drvflags[MAXLINE], drvmodes[MAXLINE];
gchar tmp[MAXLINE];
gint isdvd,i;

	isdvd = 0;
	i = get_writerreaderdevs_index(devnr);

	if (i >= 0 && curset.isProDVD && writerreaderdevs[i]->is_dvdwriter) {
	        if (convert_devnr2busid(devnr,tmp) != 0) {
	                g_error("non existing cdrom?");
	        }
		getmodesflags(tmp, drvflags, drvmodes);

		/* if drvflags contains the flag DVD we have a DVD loaded */
		if (strstr(drvflags,"DVD ")) {
			isdvd = 1;
		}
	}
	if (isdvd) {
		return _("Empty DVD+-R/RW");
	} else {
		return _("Empty CD-R/RW");
	}
}


/*
 * return please-insert-media text - CD or DVD version
 */
gchar *pleaseinsertmedia() {

        if (curset.cdrtype < 1000) {
		return _("Please insert a CD-R/RW in the CD-Writer.");
	} else {
		return _("Please insert a DVD+-R/RW in the DVD-Writer.");
	}
}


/*
 * return if we can write dvds with the given device
 */
gint is_dvdwriter(gint devnr) {
gint i;

	i = get_writerreaderdevs_index(devnr);

	if (i >= 0 && writerreaderdevs[i]->is_dvdwriter) {
		return 1;
	}
	return 0;
}


/*
 * return a new dialog widget
 */
GtkWidget *my_gtk_dialog_new() {
GtkWidget *win;

	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_realize(win);

	return(win);
}


/*
 * convert a given string to utf8, when it is not already
 * used for strings we got from the system or from cddb
 */
gchar *convert_for_gtk2(gchar *str) {
gchar *utfstr;
gsize in, out; 
	
	if (g_utf8_validate(str, strlen(str), NULL)) {
		/* we are already uft8? */
		return str;
	}

	utfstr = NULL;
	utfstr = g_locale_to_utf8(str, strlen(str), &in, &out, NULL);

	if (utfstr) {
		/* overwrite old string with new */
		strncpy(str,utfstr,MAXLINE);
	}
	return str;
}


/*
 * get a filename from an GTK2 widget
 */
gchar *convert_for_gtk2_filename(gchar *str) {

	/* no longer needed? */
	return str;
}


/*
 * return the gtk2 stock icon fitting to our requested self made icon
 */
gchar *lookup_stock_icon(gchar *icon) {

	if (strncmp(icon, ICO_ERROR, MAXLINE) == 0) 
		return GTK_STOCK_DIALOG_ERROR; 
	if (strncmp(icon, ICO_INFO, MAXLINE) == 0) 
		return GTK_STOCK_DIALOG_INFO; 
	if (strncmp(icon, ICO_WARN, MAXLINE) == 0) 
		return GTK_STOCK_DIALOG_WARNING; 
	if (strncmp(icon, ICO_QUEST, MAXLINE) == 0) 
		return GTK_STOCK_DIALOG_QUESTION; 

	/* not found? */
	return NULL;
}

