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

#include <stdio.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 <sys/time.h>
#include <errno.h>
#include <ctype.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"


/*
 * return 1 if path is a directory and 0 if not or invalid path
 */
gint is_directory(gchar *path) {
struct stat buf;

	if (stat(path,&buf) != 0) {
		return 0;
	}

	if (S_ISDIR(buf.st_mode) == 1) {
		return 1;
	} else {
		return 0;
	}
}

/*
 * return 1 if path is not a directory and 0 if or invalid path
 */
gint is_not_directory(gchar *path) {
struct stat buf;

	if (stat(path,&buf) != 0) {
		return 0;
	}

	if (S_ISDIR(buf.st_mode) == 0) {
		return 1;
	} else {
		return 0;
	}
}


/*
 * return 1 if path is a file or directory and 0 if not or invalid path
 */
gint is_file(gchar *path) {
struct stat buf;

	if (stat(path,&buf) != 0) {
		return 0;
	}
	return 1;
}


/*
 * return the base directory of a given path, or NULL when invalid
 * warning..overwrites original str
 */
gchar *get_basedir(gchar *dir) {
gchar *p;

	/* nothing to do if this is already a directory */
	if (is_directory(dir)) {
		return dir;
	}

	/* look for last slash */
	p = rindex(dir,'/');
	if (p) {
		*p = '\0';
		if (strcmp(dir,"") == 0) {
			strcpy(dir,"/");
		}
		return dir;
	}
	return NULL;
}


/*
 * return the pure filename of a given path
 */
gchar *get_purefile(gchar *dir, gchar *out) {
gchar *p;

	/* look for last slash */
	p = rindex(dir,'/');
	if (p) {
		strncpy(out, p+1, MAXLINE);
	} else {
		strncpy(out, dir, MAXLINE);
	}
	
	return out;
}


/*
 * return 1 when path contains a subdir, else 0
 * skip . and ..-directories
 */
gint is_subdirs(gchar *path) {
struct dirent *ent;
DIR *dir;         
gchar tmp[MAXLINE];

	dir = opendir(path);
	
	/* invalid directory */
	if (dir == NULL) 
		return 0;

	while ( (ent = readdir(dir)) ) {
		strcpy(tmp,path);
		/* add slash when not there */
		if (tmp[strlen(tmp)-1] != '/')  
			strcat(tmp,"/");
		strcat(tmp,ent->d_name);

		if (strcmp(ent->d_name,".") == 0 || 
		    strcmp(ent->d_name,"..") == 0) 
			continue;

		if (is_directory(tmp)) {
			closedir(dir);
			return 1;
		}
	}
	closedir(dir);
	return 0;
}


/*
 * return 1 when path contains files, else 0
 * skip . and ..-directories
 */
gint is_subfiles(gchar *path) {
struct dirent *ent;
DIR *dir;         
gchar tmp[MAXLINE];

	dir = opendir(path);
	
	/* invalid directory */
	if (dir == NULL) 
		return 0;

	while ( (ent = readdir(dir)) ) {
		strcpy(tmp,path);
		/* add slash when not there */
		if (tmp[strlen(tmp)-1] != '/')  
			strcat(tmp,"/");
		strcat(tmp,ent->d_name);

		if (strcmp(ent->d_name,".") == 0 || 
		    strcmp(ent->d_name,"..") == 0) 
			continue;

		closedir(dir);
		return 1;
	}
	closedir(dir);
	return 0;
}


/*
 * strip off a string of leading and trailing whitespace
 */
gchar *strip_string(gchar *str) {
gint i,j;
gint c1;

        if ( str == NULL) return (NULL);

        /* count how many leading chars to be whitespace */
        for(i=0; i<strlen(str); i++) {
                if (str[i] != ' ' && str[i] != '\t' && str[i] != '\r') 
                        break;
        }

        /* count how many trailing chars to be whitespace */
        for(j=strlen(str)-1; j >= 0; j--) {
                if (str[j] != ' ' && str[j] != '\t' && str[j] != '\n' && str[j] != '\r')
                        break;
        }

	/* string contains only whitespace? */
	if (j<i) {
		str[0] = '\0';
		return(str);
	}

        /* now move the chars to the front */
        for(c1=i; c1 <= j; c1++) {
                str[c1-i] = str[c1]; 
        }
        str[j+1-i] = '\0';      

        return(str);
}


/*
 * parse escape-chars in string -> e.g. translate \n to newline
 */
gchar *escape_parse(gchar *str) {
gchar tmp[MAXLINE];
gchar c;
guint i,j;

        if ( str == NULL) return (NULL);

	j = 0;
        for(i=0; i<strlen(str); i++) {
		c = str[i];
		if (c == '\\') {
			i++;
			switch(str[i]) {

			case 'n':
				c = '\n';
				break;
			
			case 't':
				c = '\t';
				break;
	
			case 'b':
				c = '\b';
				break;

			default:
				c = str[i];
			}
		}	

		tmp[j]=c;
		j++;
        }

	tmp[j] = '\0';

	strcpy(str,tmp);
        return(str);
}


/*
 * convert escape-chars in string -> e.g. newline to \n
 */
gchar *convert_escape(gchar *str) {
gchar tmp[MAXLINE*2];
gchar c,d;
guint i,j;

	if (str == NULL) return (NULL);

	j = 0;
	for (i = 0; i < strlen(str); i++) {
		c = str[i];
		d = '\0';

		switch (c) {
		case '\n':
			d = 'n';
			break;
		case '\t':
			d = 't';
			break;
		case '\b':
			d = 'b';
			break;
		case '\\':
			d = '\\';
			break;
		case '\"':
			d = '\"';
			break;
		case '=':
			d = '=';
			break;
		}
		if (d != '\0') {
			/* generate escaped char */
			tmp[j] = '\\'; j++;
			tmp[j] = d; j++;
		} else {
			tmp[j] = c; j++;
		}
	}

	tmp[j] = '\0';

	/* check if new string is not getting too long */
	if (strlen(tmp) < MAXLINE) {
		strcpy(str,tmp);
	} else {
		/* over MAXLINE chars - should never happen */
		/* cut off string */
		strncpy(str,tmp,MAXLINE-1);
		str[MAXLINE-1] = '\0';
	}

	return(str);
}


/*
 * another variant of escaping. Just required to escape = on the command
 * line of mkisofs directly
 */
gchar *convert_escape3(gchar *str) {
gchar tmp[MAXLINE*2];
gchar c,d;
guint i,j;

	if (str == NULL) return (NULL);

	j = 0;
	for (i = 0; i < strlen(str); i++) {
		c = str[i];
		d = '\0';

		switch (c) {
		case '\n':
			d = 'n';
			break;
		case '\t':
			d = 't';
			break;
		case '\b':
			d = 'b';
			break;
		case '\\':
			d = '\\';
			break;
		case '\"':
			d = '\"';
			break;
		case '=':
			d = '=';
			break;
		}
		if (d != '\0') {
			/* generate escaped char */
			tmp[j] = '\\'; j++;
			/* escape the =-sign double! */
			if (d == '=') {
				tmp[j] = '\\'; j++;
			}
			tmp[j] = d; j++;
		} else {
			tmp[j] = c; j++;
		}
	}

	tmp[j] = '\0';

	/* check if new string is not getting too long */
	if (strlen(tmp) < MAXLINE) {
		strcpy(str,tmp);
	} else {
		/* over MAXLINE chars - should never happen */
		/* cut off string */
		strncpy(str,tmp,MAXLINE-1);
		str[MAXLINE-1] = '\0';
	}
	return(str);
}


/*
 * convert escape-chars in string -> e.g. newline to \n
 * this versions will not escape quotes..required for master-path-lists
 */
gchar *convert_escape2(gchar *str) {
gchar tmp[MAXLINE*2];
gchar c,d;
guint i,j;

	if (str == NULL) return (NULL);

	j = 0;
	for (i = 0; i < strlen(str); i++) {
		c = str[i];
		d = '\0';

		switch (c) {
		case '\n':
			d = 'n';
			break;
		case '\t':
			d = 't';
			break;
		case '\b':
			d = 'b';
			break;
		case '\\':
			d = '\\';
			break;
		case '=':
			d = '=';
			break;
		}
		if (d != '\0') {
			/* generate escaped char */
			tmp[j] = '\\'; j++;
			tmp[j] = d; j++;
		} else {
			tmp[j] = c; j++;
		}
	}

	tmp[j] = '\0';

	/* check if new string is not getting too long */
	if (strlen(tmp) < MAXLINE) {
		strcpy(str,tmp);
	} else {
		/* over MAXLINE chars - should never happen */
		/* cut off string */
		strncpy(str,tmp,MAXLINE-1);
		str[MAXLINE-1] = '\0';
	}
	return(str);
}


/*
 * check if there are illegal chars in our string and replace them with _
 * return 0 if all ok, and 1 if any chars changed/removed
 */
gint remove_illegal_chars(gchar *str) {
guint i,j;

	if (str == NULL) return 0;

	j = 0;
	for (i = 0; i < strlen(str); i++) {
		switch (str[i]) {
		case '/':
		case '\\':
		case '\b':
		case '\t':
			str[i] = '_';
			j = 1;
			break;
		}
	}

	return(j);
}


/*
 * check if there are illegal chars in our iconv charset string and remove them quietly
 * for safety reasons, only letters, numbers and . : - _ / ( ) are allowed
 * mkisofs warns itself if the entered customer i/o-charset doesn't match
 */
void remove_illegal_chars_iconv(gchar *str) {
guint i, j, len;
gchar tmp[MAXLINE];

	j = 0;
	len = strlen(str); 

	if (len >= MAXLINE-1)
		len = MAXLINE-1;
 
	if (str != NULL) {
		for (i = 0; i < len; i++) {
			if ( ( str[i] >= 'A' && str[i] <= 'Z' )
			  || ( str[i] >= 'a' && str[i] <= 'z' )
			  || ( str[i] >= '0' && str[i] <= '9' )
			  || str[i] == '.' 
			  || str[i] == ':' 
			  || str[i] == '-' 
			  || str[i] == '_' 
			  || str[i] == '/' 
			  || str[i] == '(' 
			  || str[i] == ')' ) {
				tmp[j] = str[i];
				j++;
			}
		}
		tmp[j] = '\0';
		strcpy(str, tmp);
	}
}


/*
 * get a string of the form "'xxx' from 'xxx'" and extract the strings xxx
 * to artist and title. remove any escape characters.
 * Return 0 if all ok, 1 on problems
 */
gint decode_title_artist(gchar *str, gchar *title, gchar *artist) {
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];
gchar convtmp[MAXLINE];
gchar c, oldc;
guint i;

	if (str == NULL) {
		return 1;
	}

	oldc = '\0';

	/* check first char */
	if (str[0] != '\'') {
		/* not right - abort */
		return 1;
	}
	strcpy(tmp,str+1);

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

	/* look for next unescaped quote */
	for (i = 0; i < strlen(tmp); i++) {
		c = tmp[i];

		/* if current char ' and previous not \ we are done */
		if ((c == '\'') && (oldc != '\\')) {
			break;
		}
		oldc = c;
	}

	if (i == strlen(tmp)) {
		/* no quote found at all */
		return 1;
	}

	/* done extracting the title */
	strncpy(title,tmp,i);
	title[i] = '\0';
	/* now parse all other escaped chars */
	escape_parse(title);

	/* now the artist-field */
	strcpy(tmp2, tmp+i);
	if (strncmp(tmp2,"' from '",8) != 0) {
		/* look for middle string */
		/* not fitting - abort */ 
		return 1;
	}
	strcpy(tmp,tmp2+8);

	/* look for next unescaped quote */
	for (i = 0; i < strlen(tmp); i++) {
		c = tmp[i];

		/* if current char ' and previous not \ we are done */
		if ((c == '\'') && (oldc != '\\')) {
			break;
		}
		oldc = c;
	}

	if (i == strlen(tmp)) {
		/* no quote found at all */
		return 1;
	}

	/* done extracting the artist */
	strncpy(artist,tmp,i);
	artist[i] = '\0';
	/* now parse all other escaped chars */
	escape_parse(artist);

	return 0;
}


/*
 * read a single char from a file descriptor;
 * If the descriptor says that it is not available,
 * then try again up to 5 times before giving up.
 * This was required for Mac OS X 10.3 but is obsolete now.
 */
static gint read_char(gint fd, gchar *c) {
gint rc;
gint retries;

        retries = 5;

        while (retries) {
                rc = read(fd, c, 1);

                /* all ok, read one char or EOF */
                if (rc != -1) {
                        return rc;
                }

                /* error code, try again after a little while */
                usleep(100);
                retries--;
        }
        return rc;
}


/*
 * Read a line from a descriptor.  Read the line one byte at a time,
 * looking for the newline. Works fine in nonblocking mode..here
 * we return when no more data can be read. 
 * We overwrite the newline with a null.
 * We return the number of characters up to, but not including,
 * the null (the same as strlen(3)).
 */
gint read_line(gint fd, gchar *ptr, gint maxlen) {
gint n, rc;
gchar c;
gchar *str;

	str = ptr;
 
        for (n = 1; n < maxlen; n++) {
                if ( (rc = read_char(fd, &c)) == 1) {
                        *ptr++ = c;
                        if (c == '\n') {
                                break;
                        }

                } else if (rc == 0) {
			/* EOF */
                        if (n == 1)
                                return(0);      /* EOF, no data read */
                        else
                                break;          /* EOF, some data was read */
                } else if (rc == -2) {
			/* timeout while reading string */
			return(-2);
		} else {
			/* nonblocking mode an nothing to read? */
			if (rc == -1 && errno == EAGAIN) {
				if (n == 1) 
					return(-1);
				else
					break;
			}	
                        return(-1);     /* error */
		}
        }

	/* terminate the string */
	*ptr = 0;
 
        /*
	 * strip off some trailing chars
	 * yes, we need both levels
	 */
	if (strlen(str) > 0) {
        	ptr--;
        	if ((*ptr == '\n') || (*ptr == '\r') ) {
                	*ptr = 0;
        	}
	}
	if (strlen(str) > 0) {
        	ptr--;
        	if ((*ptr == '\n') || (*ptr == '\r') ) {
                	*ptr = 0;
        	}
	}
 
	if (strlen(str) == 0) {
		/* if we read an empty string, but are NOT at EOF return 1 */
		return 1;
	} else {
        	return(strlen(str));
	}
}


/*
 * Read all available output from GIOChannel, remove the training newline.
 * We return the number of characters up to, but not including,
 * the null (the same as strlen(3)).
 */
gint read_io_channel_all(GIOChannel *channel, gchar *ptr, gint maxlen) {
GError *err = NULL;
GIOStatus ret;
gsize len;
gchar *str, *p;
gchar buf[MAXLINE];

	/* remember start of return pointer */
	str = ptr;
	ret = g_io_channel_read_chars(channel, buf, MAXLINE, &len, &err);
	if (ret == G_IO_STATUS_ERROR) {
		g_error("Error reading from io channel: %s\n", err->message);
		strcpy(ptr, "");
		return 0;
	}
	if (len > 0) {
		/* find last newline and overwrite (to remove junk) */
		if ((p = strrchr(buf, '\n')) != NULL)
			*p = '\0';
		strncpy(ptr, buf, maxlen);
	} else {
		/* nothing read? (happens in non-block mode) */
		strcpy(ptr, "");
		return 0;
	}

	/* point to the end */
	ptr = ptr + strlen(str);

	/* terminate the string */
	*ptr = 0;
 
        /*
	 * strip off some trailing chars
	 * yes, we need both levels
	 */
	if (strlen(str) > 0) {
        	ptr--;
        	if ((*ptr == '\n') || (*ptr == '\r') ) {
                	*ptr = 0;
        	}
	}
	if (strlen(str) > 0) {
        	ptr--;
        	if ((*ptr == '\n') || (*ptr == '\r') ) {
                	*ptr = 0;
        	}
	}

	return (strlen(str));
}

/*
 * Read a single line from GIOChannel, remove the training newline.
 * We return the number of characters up to, but not including,
 * the null (the same as strlen(3)).
 */
gint read_io_channel(GIOChannel *channel, gchar *ptr, gint maxlen) {
GError *err = NULL;
GIOStatus ret;
gsize len;
gchar *msg;
gchar *str;

	/* remember start of return pointer */
	str = ptr;
	ret = g_io_channel_read_line(channel, &msg, &len, NULL, &err);
	if (ret == G_IO_STATUS_ERROR) {
		g_error("Error reading from io channel: %s\n", err->message);
		strcpy(ptr, "");
		return 0;
	}
	if (len > 0) {
		strncpy(ptr, msg, maxlen);
	} else {
		/* nothing read? (happens in non-block mode) */
		strcpy(ptr, "");
		return 0;
	}

	/* point to the end */
	ptr = ptr + strlen(str);
	g_free(msg);

	/* terminate the string */
	*ptr = 0;
 
        /*
	 * strip off some trailing chars
	 * yes, we need both levels
	 */
	if (strlen(str) > 0) {
        	ptr--;
        	if ((*ptr == '\n') || (*ptr == '\r') ) {
                	*ptr = 0;
        	}
	}
	if (strlen(str) > 0) {
        	ptr--;
        	if ((*ptr == '\n') || (*ptr == '\r') ) {
                	*ptr = 0;
        	}
	}

	return (strlen(str));
}


/*
 * Use this version to work around some of the critical races
 * such as by 127658. This issue should be solved on a case by case basis.
 * Expected side effects: GUI freezes (e.g. if called from read_write_out() in io.c).
 */
/*** No longer used ***/

gint read_line_wait(gint fd, gchar *ptr, gint maxlen) {
gint n, rc;
gchar c;
gchar *str;

	str = ptr;
 
        for (n = 1; n < maxlen; n++) {
                if ( (rc = read(fd, &c, 1)) == 1) {
                        *ptr++ = c;
                        if (c == '\n') {
                                break;
                        }

                } else if (rc == 0) {
			/* EOF */
                        if (n == 1)
                                return(0);      /* EOF, no data read */
                        else
                                break;          /* EOF, some data was read */
                } else if (rc == -2) {
			/* timeout while reading string */
			return(-2);
		} else {
			/* nonblocking mode an nothing to read? */
			if (rc == -1 && errno == EAGAIN) {
				if (n == 1) 
					return(-1);
				else {
					/*
					 * Partial line read. Wait a bit longer
					 * in case there's more. This does not fix
					 * all the races in the parsing code,
					 * but works around them somewhat.
					 */
					struct timeval t;
					fd_set set;
					FD_ZERO(&set);
					FD_SET(fd,&set);
					t.tv_sec = 1;
					t.tv_usec = 0;
					if (select (fd+1, &set, NULL,
						    NULL, &t) > 0)
						continue;

					break;
				}	
			}	
                        return(-1);     /* error */
		}
        }

	/* terminate the string */
	*ptr = 0;
 
        /*
	 * strip off some trailing chars
	 * yes, we need both levels
	 */
	if (strlen(str) > 0) {
        	ptr--;
        	if ((*ptr == '\n') || (*ptr == '\r') ) {
                	*ptr = 0;
        	}
	}
	if (strlen(str) > 0) {
        	ptr--;
        	if ((*ptr == '\n') || (*ptr == '\r') ) {
                	*ptr = 0;
        	}
	}
 
	if (strlen(str) == 0) {
		/* if we read an empty string, but are NOT at EOF return 1 */
		return 1;
	} else {
        	return(strlen(str));
	}
}


/*
 * extract quotes-delimted string after first colon
 * e.g.: 03: "bla" -> bla
 */
gint extract_quoted(gchar *str) {
gchar *p, *p2;
gchar tmp[MAXLINE];
gchar tmp2[MAXLINE];

	strcpy(tmp,str);

	/* get string after first colon */
	p = strtok(tmp,":");
	if (p == NULL)
		return 1;
	p = strtok(NULL,"");
	if (p == NULL) 
		return 1;

	strcpy(tmp,p);
	strip_string(tmp);

	/* now strip off quotes */
	p = tmp;
	if (*p == '\"') {
		p2 = p+1;
	} else {
		p2 = p;
	}
	if (p[strlen(p)-1] == '\"') {
		p[strlen(p)-1] = '\0';
	}
	strcpy(tmp2,p2);
	escape_parse(tmp2);

	strcpy(str,tmp2);

	return 0;
}


/*
 * extract single quotes-delimted string
 * e.g.: 'bla' -> bla
 */
gint extract_singlequoted(gchar *str) {
gchar *p, *p2;
gchar tmp[MAXLINE];

	strcpy(tmp,str);
	strip_string(tmp);

	/* now strip off quotes */
	p = tmp;
	if (*p == '\'') {
		p2 = p+1;
	} else {
		p2 = p;
	}
	if (p[strlen(p)-1] == '\'') {
		p[strlen(p)-1] = '\0';
	}
	strcpy(str,p2);

	return 0;
}


/*
 * read one char from a file descriptor with timeout
 * return 1 if char read ok, -1 on read error, -2 on timeout
 */
gint get_char(gint fd, char *c) {
gint j;
struct timeval t;
fd_set set;

	FD_ZERO(&set);
	FD_SET(fd,&set);
	t.tv_sec = NETIOTIMEOUT;
	t.tv_usec = 0;

	j = select(fd+1, &set, NULL, NULL, &t);
	if (j > 0) {
		return(read(fd, c, 1));
	} else {	
		/* timeout triggered */
		return -2;
	}
}


/*
 * Read a line from a descriptor.  Read the line one byte at a time,
 * looking for the newline.  
 * We overwrite the newline with a null.
 * We return the number of characters up to, but not including,
 * the null (the same as strlen(3)).
 */
gint read_line2(gint fd, gchar *ptr, gint maxlen, gint timeout) {
gint n, rc;
gchar c;
 
        for (n = 1; n < maxlen; n++) {
		if (timeout) {
			/* use timeout */
			rc = get_char(fd,&c);
		} else {
			rc = read(fd, &c, 1);
		}	
		if ( rc == 1) {
                        *ptr++ = c;
                        if (c == '\n') {
                                break;
                        }
                } else if (rc == 0) {
                        if (n == 1)
                                return(0);      /* EOF, no data read */
                        else
                                break;          /* EOF, some data was read */
                } else if (rc == -2) {
			/* timeout while reading string */
			return(-2);
		} else {
                        return(-1);     /* error */
		}
        }
 
        /*
	 * strip off some trailing chars
	 * yes, we need all three levels
	 */
        if ((*ptr == '\n') || (*ptr == '\r') ) {
                *ptr = 0;
        }
        ptr--;
        if ((*ptr == '\n') || (*ptr == '\r') ) {
                *ptr = 0;
        }
        ptr--;
        if ((*ptr == '\n') || (*ptr == '\r') ) {
                *ptr = 0;
        }
 
        return(n);
}

 
/*
 * Write "n" bytes to a descriptor.
 * Use in place of write() when fd is a stream socket.
 */
gint writen(gint fd, gchar *ptr, gint nbytes, gint newline) {
gint nleft, nwritten;
 
        nleft = nbytes;
        while (nleft > 0) {
                nwritten = write(fd, ptr, nleft);
                if (nwritten <= 0)
                        return(nwritten);               /* error */
 
                nleft -= nwritten;
                ptr   += nwritten;
        }
 
        /* add a newline when requested */
        if (newline) {
                nwritten = write(fd,"\n",1);
        }
 
        return(nbytes - nleft);
}


/*
 * replace ~/ in an entry field by the home-directory
 * warning - in some cases $HOME is not correct when user root
 */
gchar *check_tilde(gchar *str) {
gchar tmp[MAXLINE];

	if (str == NULL) 
		return str;

	strip_string(str);

	/* too short, do nothing */
	if (strlen(str) < 2) 
		return str;

	/* ~/ found? */
	if (str[0] == '~' && str[1] == '/') {
		tmp[0] = '\0';
		if (!isroot()) {
			/* we are not root, trust $HOME */
			if (g_get_home_dir()) {
				strcpy(tmp, g_get_home_dir());
			}
		} else {
			/* as root $HOME is often wrong - override */
			if (get_pw_home(0)) {
				strcpy(tmp, get_pw_home(0));
			}
		}
		strcat(tmp,"/");
		strcat(tmp,str+2);

		strcpy(str,tmp);
	}

	return str;
}


/*
 * parse config line and return id and value
 * return 0 if ok, 1 on error
 */
gint parse_config_line(gchar *iline, gchar *id, gchar *value) {
gchar *p,*p2;
gchar line[1024];
gchar tmp[1024];

	strncpy(line,iline, MAXLINE); 
	strcpy(id,"");
        p = strtok(line,"=");
        if (p != NULL) {
		/* got id */
        	strcpy(id,p);
                strip_string(id);
        } else {
		return 1;
	}

	strcpy(tmp,"");
        p = strtok(NULL,"");
        if (p != NULL) {
		/* string after = */
        	strcpy(tmp,p);
                strip_string(tmp);
        } else { 
		return 1;
	}

        /* now strip off quotes from string */
        p = tmp;
        if (*p == '\"') {
                p2 = p+1;
        } else {
                p2 = p;
        }
        if (p[strlen(p)-1] == '\"') {
                p[strlen(p)-1] = '\0';
        }
        strcpy(value,p2);

        /* now reconvert escape-chars */
        escape_parse(value);

	/* all ok */
	return 0;
}


/*
 * parse config line and return id and value, value2
 * form is  ID = "val1","val2"
 * return 0 if ok, 1 on error
 */
gint parse_config_line2(gchar *iline, gchar *id, gchar *value, gchar *value2) {
gchar *p,*p2;
gchar line[1024];
gchar tmp[1024];

	strncpy(line,iline,MAXLINE); 
	strcpy(id,"");
	strcpy(value,"");
	strcpy(value2,"");
        p = strtok(line,"=");
        if (p != NULL) {
		/* got id */
        	strcpy(id,p);
                strip_string(id);
        } else {
		return 1;
	}

	strcpy(tmp,"");
        p = strtok(NULL,"");
        if (p != NULL) {
		/* string after = */
        	strcpy(tmp,p);
                strip_string(tmp);
        } else { 
		return 1;
	}

        /* now strip off quotes from string */
        p = tmp;
        if (*p == '\"') {
                p2 = p+1;
        } else {
                p2 = p;
        }
        if (p[strlen(p)-1] == '\"') {
                p[strlen(p)-1] = '\0';
        }
        strcpy(line,p2);

	/* now in line is someling like xxx","yyy */
	p = strstr(line,"\",\"");
	if (p) {
		*p = '\0';
		strcpy(value,line);
		strcpy(value2,p+3);
	}

        /* now reconvert escape-chars */
        escape_parse(value);
        escape_parse(value2);

	/* all ok */
	return 0;
}


/*
 * check if we are root
 */
gint isroot() {

	if (geteuid() == 0) {
		return 1;
	} else {
		return 0;
	}
}


/*
 * check if user exists
 */
gint check_pw_user(gchar *name) {
struct passwd *ent;

	ent = getpwnam(name);
	if (ent) return 1;
	return 0;
}


/*
 * return the homedir (as in /etc/passwd) for a given user
 */
gchar *get_pw_home(gint uid) {
struct passwd *ent;

	ent = getpwuid(uid);

	if (ent) 
		return ent->pw_dir;
	else
		return NULL;
}


/*
 * return the owner (uid) of a given file
 */
gint get_file_owner(gchar *path) {
struct stat buf;

	if (stat(path,&buf) != 0) {
		return -1;
	}

	return (buf.st_uid);
}


/*
 * does move a text file
 */
gint move_textfile(gchar *src, gchar *target) {
FILE *fd, *fd2;
gchar line[MAXLINE];

	fd = fopen(src,"r");
	if (!fd) {
		/* src file failed to open */
		return 1;
	}

	fd2 = fopen(target,"w");
	if (!fd2) {
		/* target file failed to open */
		fclose(fd);
		return 1;
	}

	/* copy file line by line */
	for (;;) {
		if (fgets(line,MAXLINE,fd) == NULL)
			break;
	
		fputs(line,fd2);
	}

	fclose(fd2);
	fclose(fd);

	/* now delete the src file */
	unlink(src);

	return 0;
}


/*
 * checks if a given file is a link. If so return 1
 * return the value of the link too, if requested
 */
gint check_islink(gchar *file, gchar *link) {
struct stat buf;

	if (lstat(file, &buf) == 0) {
		if (S_ISLNK(buf.st_mode)) {
			if (link) {
				memset(link, 0, MAXLINE); 
				if (readlink(file, link, MAXLINE-1) <= 0) {
					strcpy(link, "?");
				}
			}
			return 1;
		}
	}

	return 0;
}


/*
 * extract a readable string from a buffer
 */
void get_subheader(gchar *buf, gchar *text, gint start, gint end) {
gchar tmp[MAXLINE];
gchar c;
gint i,count;

        count = 0;
        for(i = start; i < end; i++) {
                c = buf[i];
                if (isprint((gint)c) || isspace((gint)c)) {
                        tmp[count++] = c;
                }
        }
        tmp[count] = '\0';
        
        for(i = strlen(tmp)-1; i >= 0; i--) {
                if(tmp[i] != 0 && tmp[i] != ' ') {
                        tmp[i+1] = '\0';
                        break;
                }
        }
	      
	strip_string(tmp);
        strcpy(text, tmp);
}


/*
 * URL decoding - used because drag&drop names are often URL encoded
 * borrowed from php4 source code (php4/ext/standard/url.c)
 */
static gint url_htoi(gchar *s) {
gint value;
gint c;

        c = s[0];
        if (isupper(c))
                c = tolower(c);
        value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;

        c = s[1];
        if (isupper(c))
                c = tolower(c);
        value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;

        return (value);
}


gchar *url_decode(gchar *str, gint len) {
gchar *dest = str;
gchar *data = str;

	while (len--) {
                if (*data == '+')
			*dest = ' ';
		else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) {
                        *dest = (gchar) url_htoi(data + 1);
                        data += 2;
                        len -= 2;
                } else
                        *dest = *data;
                data++;
                dest++;
	}
        *dest = '\0';
        return dest;
}	


/*
 * URL encoding - used because drag&drop names are often URL encoded
 * borrowed from php4 source code (php4/ext/standard/url.c)
 */
static unsigned char hexchars[] = "0123456789ABCDEF";

/*
 * encode all special chars, but not / - and .
 */
gchar *url_encode(gchar *s, gint len, gint *new_length) {
gint x, y;
guchar *str;

	str = (guchar *) g_new0(guchar *, 3 * len + 1);
        for (x = 0, y = 0; len--; x++, y++) {
                str[y] = (guchar) s[x];
                if (str[y] == ' ') {
                        str[y] = '+';
                } else if ((str[y] < '0' && str[y] != '-' && str[y] != '.' && str[y] != '/') ||
                                   (str[y] < 'A' && str[y] > '9') ||
                                   (str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||
                                   (str[y] > 'z')) {
                        str[y++] = '%';
                        str[y++] = hexchars[(guchar) s[x] >> 4];
                        str[y] = hexchars[(guchar) s[x] & 15];
                }
        }
        str[y] = '\0';
        if (new_length) {
                *new_length = y;
        }
        return ((gchar *) str);
}


/*
 * extract the first filename from a set of data received from a drag 
 * operation - return 1 on success
 */
gint extract_single_drag_filename(gchar *dragtext, gint draglen, gchar *rettext) {
gchar *p;
gchar tmp[MAXLINE];
gint len;

	/* if we got serveral filenames only use the first */
	if (dragtext) {
		p = index(dragtext,'\r');	
		if (p) {
			len = p - dragtext;
			if (len < MAXLINE) {
				strncpy(tmp,dragtext,len);
				tmp[len] = '\0';
			} else {
				return 0;
			}
		} else {
			strncpy(tmp,dragtext, MAXLINE);
		}

		/* extracted a single item */
		strip_string(tmp);	

	
		/* does it begin with file:? */
		if (strncmp(tmp,"file:", 5) == 0) {
			/* two slashes at front? */
			if (strlen(tmp) > 7 && tmp[5] == '/' && tmp[6] == '/') {
				/* three slashes? */
				if (strlen(tmp) > 8 && tmp[7] == '/') {
					strncpy(rettext,tmp+7, MAXLINE);
					/* url_decode only this case */
					url_decode(rettext, strlen(rettext));
				} else {
					strncpy(rettext,tmp+6, MAXLINE);
				}
			} else {	
				strncpy(rettext,tmp+5, MAXLINE);
			}
			return 1;
		}
	}

	return 0;
}


/*
 * extract the a list of filename from a set of data received from a drag 
 * operation - return 1 on success
 */
gint extract_glist_drag_filenames(gchar *dragtext, gint draglen, gchar *match, GList **dst) {
gchar *p, *buf;
gchar tmp[MAXLINE], tmp2[MAXLINE];
gint count;

	count = 0;

	/* if we got serveral filenames only use the first */
	if (dragtext) {
		/* allocate a tmp buffer for the drag data */
		buf = g_strdup(dragtext);

		p = strtok(buf,"\r\n");	
		while (p) {
			strncpy(tmp, p, MAXLINE);
			strip_string(tmp);	

			/* does it begin with file:? */
			if (strncmp(tmp,match, 5) == 0) {
				/* two slashes at front? */
				if (strlen(tmp) > 7 && tmp[5] == '/' && tmp[6] == '/') {
					/* three slashes? */
					if (strlen(tmp) > 8 && tmp[7] == '/') {
						strncpy(tmp2,tmp+7, MAXLINE);
						/* url decode this case */
						url_decode(tmp2, strlen(tmp2));
					} else {
						strncpy(tmp2,tmp+6, MAXLINE);
					}
				} else {	
					strncpy(tmp2,tmp+5, MAXLINE);
				}
				*dst = g_list_append(*dst, g_strdup(tmp2));
				count++;
			}
			p = strtok(NULL,"\r\n");
		}
		g_free(buf);
	}

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


/*
 * return 1 when invalid MCN number
 * code from cdrtools  auinfo.c
 */
gint verify_mcn(gchar *mcn) {
gchar *p;

	/* wrong length? */
	if (strlen(mcn) != 13) {
		return 1;
	}

	for (p = mcn; *p; p++) {
		/* illegal chars in string? */
		if (*p < '0' || *p > '9') {
			return 1;
		}
	}
	return 0;
}


/*
 * return 1 when invalid ISRC number
 * code from cdrtools  auinfo.c
 */
gint verify_isrc(gchar *isrc) {
gchar upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
gchar ibuf[13];
gchar *ip, *p;
gint i, len;

	if ((len = strlen(isrc)) != 12) {
		for (p = isrc, i = 0; *p; p++) {
			if (*p == '-')
				i++;
		}
		if (((len - i) != 12) || i > 3) {
			/* illegal length */
			return 1;
		}
	}
		
	/* check country code */
        for (p = isrc, ip = ibuf, i = 0; i < 2; p++, i++) {
                *ip++ = *p;
                if (!strchr(upper, *p)) {
			/* allow numbers even when not expected */
                        if (*p >= '0' && *p <= '9')
                                continue;
			return 1;
                }
        }
        if (*p == '-')
                p++;
	
        /* owner code */
        for (i = 0; i < 3; p++, i++) {
                *ip++ = *p;
                if (strchr(upper, *p))
                        continue;
                if (*p >= '0' && *p <= '9')
                        continue;
		return 1;
        }
        if (*p == '-')
                p++;

	/* year and recording number */
        for (i = 0; i < 7; p++, i++) {
                *ip++ = *p;
                if (*p >= '0' && *p <= '9')
                        continue;
                if (*p == '-' && i == 2) {
                        ip--;
                        i--;
                        continue;
                }
		return 1;
        }

	return 0;
}


/*
 * remove one path level from a given path
 * if we are already at root level return emtpy string
 */
gchar *get_reducedpath(gchar *dir, gchar *out) {
gchar *p;
gchar tmp[MAXLINE];

	/* trailing slash? remove it first */
	strncpy(tmp, dir, MAXLINE);
	if (tmp[strlen(tmp)-1] == '/') {
		tmp[strlen(tmp)-1] = '\0';
	}

	/* look for last slash */
	p = rindex(tmp,'/');
	if (p) {
		/* cut it  */
		*p = '\0';
	}
	strncpy(out, tmp, MAXLINE);

	return out;
}


/*
 * convert latin1 charset to UTF8
 * if convert fails, return FALSE
 */
gint charset_convert_to_utf8(gchar *str, gchar *out) {
gchar *convtmp;

	/* hardcode latin -> utf-8 conversation */
	convtmp = g_convert(str, -1, "utf-8", CHARSET_CONVERT_FROM, NULL, NULL, NULL);
	if (convtmp) {
		strncpy(out, convtmp, MAXLINE);
		g_free(convtmp);
		return(TRUE);
	}
	return(FALSE);
}


/*
 * convert UTF8 charset to latin1
 * if convert fails, return FALSE
 */
gint charset_convert_to_latin1(gchar *str, gchar *out) {
gchar *convtmp;

	/* hardcode latin -> utf-8 conversation */
	convtmp = g_convert(str, -1, CHARSET_CONVERT_FROM, "utf-8", NULL, NULL, NULL);
	if (convtmp) {
		strncpy(out, convtmp, MAXLINE);
		g_free(convtmp);
		return(TRUE);
	}
	return(FALSE);
}

