
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <libgen.h>

#include "misc_utils.h"

int add_argv( char **dest, char *content )
{
	size_t i;

	i = 0;

	while ( content[ i++ ] != '\0' ) ;

	if ( ( *dest = malloc( i ) ) == NULL ) {
		err_handler( MALLOC_ERR, NULL );
		return FALSE;
	}

	strcpy( *dest, content );
	return TRUE;
}

int process_options( char *options, char **argv, int start, int end )
{
	int current, i, j, flag;
	char buf[ MAX_SINGLE_OPTION_LENGTH ];

	current = start;
	i = 0;
	while ( options[ i ] != '\0' ) {
		while ( isspace( options[ i ] ) ) i++;
		j = 0;
		flag = 0;
		while ( !isspace( options[ i ] )
			        && options[ i ] != '\0'
			        && j < MAX_SINGLE_OPTION_LENGTH - 1 ) {
			/* It really has something other than spaces */
			flag = 1;
			buf[ j++ ] = options[ i++ ];
		}
		buf[ j ] = '\0';

		/* If it really has something */
		if ( flag ) {
			if ( current < end ) {
				if ( add_argv( &argv[ current ], buf ) == FALSE )
					return - 1;
			} else {
				err_handler( TOO_MANY_ARGS_ERR, NULL );
			}
			current++;
		}
	}
	return current - start;
}

char **create_argv_for_execution_using_shell( char *command )
{
	char * shell;
	char **argv;

	shell = config.shell_for_execution;

	if ( ( argv = ( char ** ) malloc( sizeof( char * ) * 4 ) ) == NULL ) {
		err_handler( MALLOC_ERR, NULL );
		return NULL;
	}
	argv[ 0 ] = NULL;
	argv[ 1 ] = NULL;
	argv[ 2 ] = NULL;
	argv[ 3 ] = NULL;

	if ( add_argv( &( argv[ 0 ] ), shell ) == FALSE )
		return NULL;
	if ( add_argv( &( argv[ 1 ] ), "-c" ) == FALSE )
		return NULL;
	if ( add_argv( &( argv[ 2 ] ), command ) == FALSE )
		return NULL;
	return argv;
}

void free_argv( char **argv )
{
	int i;

	i = 0;
	while ( argv[ i ] != NULL )
		free( argv[ i++ ] );

	free( argv );
}

int parse_rx_format_string( char **target, char *format,
                            int track_no, char *w_fname, char *m_fname,
                            char *artist, char *album, char *song )
{
	int s, d, n, pass, totlen = 0;
	char ch;
	char *tmp, *bp;
	char track_no_str[ 4 ];

	track_no_str[ 0 ] = 0;
	if ( track_no >= 0 )
		snprintf( track_no_str, sizeof( track_no_str ), "%02d", track_no + 1 );

	for (pass = 0; pass <= 1; ++pass) {
		for (s = d = 0; format[ s ] != 0; ++s) {
			// determine space character and check for error
			if ( format[ s ] == '%' ) {
				ch = format[ s + 1 ];
				if ( ch == '\0' ) {
					return - 1;
				}
				if ( ch == '%' || ch == '#' || ch == 'w' || ch == 'm' ||
							ch == 'a' || ch == 'v' || ch == 's' )
					s += 1;
				else {
					ch = format[ s + 2 ];
					if ( ch == '\0' ) {
						return -1;
					}
					if ( ch == '%' || ch == '#' || ch == 'w' || ch == 'm' ||
								ch == 'a' || ch == 'v' || ch == 's' ) {
						s += 2;
					}
				}

				// determine what to copy
				tmp = NULL;
				switch ( format[ s ] ) {
				case '%' :
					tmp = "%";
					break;
				case '#' :
					tmp = track_no_str;
					break;
				case 'w' :
					tmp = w_fname;
					break;
				case 'm' :
					tmp = m_fname;
					break;
				case 'a' :
					tmp = artist;
					break;
				case 'v' :
					tmp = album;
					break;
				case 's' :
					tmp = song;
					break;
				}
				if ( tmp == NULL ) {
					return -1;
				}
				// expand
				n = strlen(tmp);
				if ( !pass )
					totlen += n;
				else {
					strcpy(bp + d, tmp);
					d += n;
				}
			}
			else {
				if ( !pass )
					totlen += 1;
				else
					bp[ d++ ] = format[ s ];
			}
		}
		if ( !pass ) {
			mk_buf(target, totlen + 1);
			bp = *target;
		}
	}
	bp[ d ] = '\0';
	return 0;
}

char *time_to_readable( time_t sec )
{
	static char buf[ 10 ];

	sprintf( buf, "%2d:%02d", ( int ) sec / 60, ( int ) sec % 60 );
	return buf;
}

char *length_to_readable( unsigned length )
{
	time_t sec;

	sec = ( float ) length / CD_SECTORS_PER_SEC;
	return time_to_readable( sec );
}


char *expand_tilde( char *path )
{
	static char buf[ MAX_FILE_PATH_LENGTH + MAX_FILE_NAME_LENGTH ];

	if ( path[ 0 ] != '~' )
		return path;
	strcpy( buf, getenv( "HOME" ) );
	strcpy( buf + strlen( buf ), path + 1 );
	return buf;
}

char *construct_file_name( char *path, char *name )
{
	int offset;
	static char buf[ MAX_FILE_PATH_LENGTH + MAX_FILE_NAME_LENGTH ];

	strcpy( buf, path );
	offset = strlen( buf ) - 1;
	if ( buf[ offset ] != '/' )
		buf[ ++offset ] = '/';

	offset++;

	strcpy( buf + offset, name );
	return buf;
}

char *file_name_without_path( char *src )
{
	int offset;

	offset = strlen( src ) - 1;

	while ( src[ offset ] != '/' && offset >= 0 )
		offset--;

	return src + ++offset;
}

char *file_path_without_name( char *src )
{
	static char buf[ MAX_FILE_PATH_LENGTH ];
	int offset;

	offset = strlen( src ) - 1;

	while ( src[ offset ] != '/' && offset >= 0 )
		offset--;

	strncpy( buf, src, offset );
	buf[offset] = '\0';
	return buf;
}

void auto_append_extension( char *src, int type )
{
	int offset;

	offset = strlen( src ) - 4;
	/* aasdfasdfasd.wav */
	/*             ^    */

	if ( type == WAV ) {
		if ( strcmp( ".wav", src + offset ) != 0 ) {
			offset += 4;
			strcpy( src + offset, ".wav" );
			return;
		}
	} else if ( type == OGG) {
		if ( strcmp( ".ogg", src + offset ) != 0 ) {
			offset += 4;
			strcpy( src + offset, ".ogg" );
			return;
		}
	} else if ( type == FLAC) {
 	        offset--;
  		if ( strcmp( ".flac", src + offset ) != 0 ) {
  			offset += 5;
  			strcpy( src + offset, ".flac" );
  			return;
  		}
	} else {
		if ( strcmp( ".mp3", src + offset ) != 0 ) {
			offset += 4;
			strcpy( src + offset, ".mp3" );
			return;
		}
	}
}
char *get_default_track_title( int track )
{
	static char name_buf[ MAX_FILE_NAME_LENGTH ];
	char *name_format = config.mp3_file_name_format;
	char track_no_buf[ 5 ];
	int read_offset, write_offset, return_val;

	read_offset = -1;
	write_offset = 0;
	while ( name_format[ ++read_offset ] != '\0' ) {
		if ( name_format[ read_offset ] == '%' ) {
			snprintf( track_no_buf, 5, "%d", track + 1 );
			strcpy( name_buf + write_offset, track_no_buf );
			write_offset += strlen( track_no_buf );
		} else name_buf[ write_offset++ ] = name_format[ read_offset ];
	}
	name_buf[ write_offset ] = '\0';
	return name_buf;
}

int is_str_blank( char *str )
{
	int i = 0;

	if ( !str )
		return TRUE;
	while ( str[ i++ ] != '\0' )
		if ( !isspace( str[ i ] ) && str[ i ] != '\0' )
			return FALSE;
	return TRUE;
}

int high_ascii_to_low_ascii ( unsigned char *p )
{
		/* Convert ASCII 160-255 to closest matching, ASCII within 32-126 */
		if (*p == 160) {		*p = ' ';	return 0;		}
		if (*p == 161) {		*p = 'i';	return 0;		}
		if (*p == 162) {		*p = 'c';	return 0;		}
		if (*p == 163) {		*p = 'E';	return 0;		}
		if (*p == 164) {		*p = 'o';	return 0;		}
		if (*p == 165) {		*p = 'Y';	return 0;		}
		if (*p == 166) {		*p = 'I';	return 0;		}
		if (*p == 167) {	 	*p = 'S';	return 0;		}
		if (*p == 168) {		*p = '-';	return 0;		}
		if (*p == 169) {		*p = 'c';	return 0;		}
		if (*p == 170) {		*p = 'a';	return 0;		}
		if (*p == 171) {		*p = '<';	return 0;		}
		if (*p == 172 || *p == 173) {	*p = '-';	return 0;		}
		if (*p == 174) {		*p = 'r';	return 0;		}
		if (*p == 175) {		*p = '-';	return 0;		}
		if (*p == 176) {		*p = 'o';	return 0;		}
		if (*p == 177) {		*p = '+';	return 0;		}
		if (*p == 178) {		*p = '2';	return 0;		}
		if (*p == 179) {		*p = '3';	return 0;		}
		if (*p == 180) {		*p = '.';	return 0;		}
		if (*p == 181) {		*p = 'u';	return 0;		}
		if (*p == 182) {		*p = 'P';	return 0;		}
		if (*p == 183 || *p == 184) {	*p = '.';	return 0;		}
		if (*p == 185) {		*p = '7';	return 0;		}
		if (*p == 186) {		*p = 'o';	return 0;		}
		if (*p == 187) {		*p = '>';	return 0;		}
		if (*p >= 188 && *p <= 190) {	*p = '4';	return 0;		}
		if (*p == 191) {		*p = '?';	return 0;		}
		if (*p >= 192 && *p <= 198) {	*p = 'A';	return 0;		}
		if (*p == 199) {		*p = 'C';	return 0;		}
		if (*p >= 200 && *p <= 203) {	*p = 'E';	return 0;		}
		if (*p >= 204 && *p <= 207) {	*p = 'I';	return 0;		}
		if (*p == 208) {		*p = 'D';	return 0;		}
		if (*p == 209) {		*p = 'N';	return 0;		}
		if (*p >= 210 && *p <= 214) {	*p = 'O';	return 0;		}
		if (*p == 215) {		*p = 'x';	return 0;		}
		if (*p == 216) {		*p = 'O';	return 0;		}
		if (*p >= 217 && *p <= 220) {	*p = 'U';	return 0;		}
		if (*p == 221) {		*p = 'Y';	return 0;		}
		if (*p == 222) {		*p = 'b';	return 0;		}
		if (*p == 223) {		*p = 'B';	return 0;		}
		if (*p >= 224 && *p <= 230) {	*p = 'a';	return 0;		}
		if (*p == 231) {		*p = 'c';	return 0;		}
		if (*p >= 232 && *p <= 235) {	*p = 'e';	return 0;		}
		if (*p >= 236 && *p <= 239) {	*p = 'i';	return 0;		}
		if (*p == 240) {		*p = 'o';	return 0;		}
		if (*p == 241) {		*p = 'n';	return 0;		}
		if (*p >= 242 && *p <= 246) {	*p = 'o';	return 0;		}
		if (*p == 247) {		*p = '-';	return 0;		}
		if (*p == 248) {		*p = 'o';	return 0;		}
		if (*p >= 249 && *p <= 252) {	*p = 'u';	return 0;		}
		if (*p == 253) {		*p = 'y';	return 0;		}
		if (*p == 254) {		*p = 'b';	return 0;		}
		if (*p == 255) {		*p = 'y';	return 0;		}

		return 1;
}

void remove_non_unix_chars( unsigned char *src )
{
	unsigned char *p = src, *w = p;

	for (; *p != 0; p++ ) {
	
		// Evil chars unsafe for shell
		if (	*p == '\''
			|| *p == '"'
			|| *p == ';'
			|| *p == '*'
						) {
			continue;	// Skip char, shrink string		
		}

		// Unprintable chars
		if (*p < 32  || (*p > 126 && *p < 160) ) {	
			continue;	// Skip char, shrink string
		}
		
		if (*p >= 160) {
			high_ascii_to_low_ascii(p);
		}

		*(w++) = *p;
	}
	*w = 0;	//Terminate shrunken string
}

void convert_slashes( char *src, char c )
{
	char *p;

	for (p = src; *p != 0; p++ ) {
		if (*p == '/')
			*p = c;

	}
}

void convert_spaces( char *src, char c )
{
	char *p;

	for (p = src; *p != 0; p++ ) {
		if (*p == ' ')
			*p = c;

	}
}


/***************************************************
 * 
 * Function implementations immigrated from misc.c
 *
 */


char *get_string_piece( FILE* file, int delim )
{
	/* gets one complete row from 'file' and save it in 'buffer'.
	buffer's memory will be freed and allocated to fit the stringsize
	automatically. */

	char * buffer1 = ( char* ) malloc( 1 ),
	                 *buffer2 = ( char* ) malloc( 1 ),
	                            *tmp = ( char* ) malloc( 1024 );
	char **active, **inactive;
	int i = 0;

	strcpy( buffer1, "" );
	strcpy( buffer2, "" );
	strcpy( tmp, "" );
	do {
		/*switch the frames*/
		if ( inactive == &buffer1 ) {
			active = &buffer1;
			inactive = &buffer2;
		} else {
			active = &buffer2;
			inactive = &buffer1;
		}
		/*get the next part, and handle EOF*/
		if ( fgets( tmp, 1024, file ) == NULL ) { /* ok, so we reached the end of the
									            			                                                                                    																 file w/o finding the delimiter */
			free( *active );
			return NULL;
		}

		free( *active );
		*active = ( char* ) malloc( ( ++i ) * 1024 );
		sprintf( *active, "%s%s", *inactive, tmp );

	} while ( strchr( *active, delim ) == NULL );

	free( *inactive );
	return *active;
}

char *get_ascii_file( FILE* file )
{
	/* gets one complete row from 'file' and save it in 'buffer'.
	buffer's memory will be freed and allocated to fit the stringsize
	automatically. */

	char * buffer1 = ( char* ) malloc( 1 ),
	                 *buffer2 = ( char* ) malloc( 1 ),
	                            *tmp = ( char* ) malloc( 1024 );
	char **active, **inactive;
	int i = 0;

	strcpy( buffer1, "" );
	strcpy( buffer2, "" );
	strcpy( tmp, "" );
	do {
		/*switch the frames*/
		if ( inactive == &buffer1 ) {
			active = &buffer1;
			inactive = &buffer2;
		} else {
			active = &buffer2;
			inactive = &buffer1;
		}
		/*get the next part, and handle EOF*/
		if ( fgets( tmp, 1024, file ) == NULL ) {
			free( *active );
			return *inactive;
		}

		free( *active );
		*active = ( char* ) malloc( ( ++i ) * 1024 );
		sprintf( *active, "%s%s", *inactive, tmp );
	} while ( 1 );
}

void strip_trailing_space( char **string )
{
	int i = strlen( *string ) - 1;
	char *return_string;

	if ( string == NULL || *string == NULL )
		return;
	while ( isspace( ( *string ) [ i ] ) )
		i--;
	i++;
	return_string = ( char* ) malloc( i + 1 );
	strncpy( return_string, *string, i ); return_string[ i ] = 0;
	free( *string );
	*string = return_string;
}

void strip_leading_space( char **string )
{
	char * tmp = *string,
	             *return_string;

	if ( string == NULL || *string == NULL )
		return;

	while ( isspace( *tmp ) )
		tmp++;

	return_string = strdup( tmp );
	free( *string );
	*string = return_string;
}


char *string_append( char **dest, char *appendage )
{
	char * holder;

	if ( dest == NULL || appendage == NULL )
		return NULL;

	if ( *dest != NULL ) {
		holder = *dest;
		*dest = ( char* ) malloc( strlen( holder ) + strlen( appendage ) + 1 );
		sprintf( *dest, "%s%s", holder, appendage );
		free( holder );
	} else
		*dest = strdup( appendage );

	return *dest;
}

void charpp_to_charp( char **dest, char**src, int num, char* separator )
{
	int i, size = 0, sep_size = 0;

	if ( src == NULL || num == 0 )
		return;

	if ( separator == NULL )
		separator = strdup( "" );

	sep_size = strlen( separator );

	for ( i = 0; i < num; i++ )
		size += strlen( src[ i ] ) + sep_size;
	if ( *dest != NULL )
		free( *dest );

	*dest = ( char* ) malloc( size + 1 );
	strcpy( *dest, "" );

	for ( i = 0; i < num - 1; i++ ) {
		strcat( *dest, src[ i ] );
		strcat( *dest, separator );
	} /* do it this way, so no separator will get in as the last part */
	strcat( *dest, src[ num - 1 ] );

}

FILE *socket_init( const char *server, short int port )
{
	struct hostent * host;
	struct sockaddr_in socket_address;
	int hsocket;

	/* see if some default values have been set */
	if ( server == NULL ) {
		return NULL;
	}

	host = gethostbyname( server );
	if ( host == NULL )
		return NULL;          /* we don't know the host */

	/* set socket address */
	memset( &socket_address, 0, sizeof( socket_address ) );
	memcpy( ( char* ) & socket_address.sin_addr, host->h_addr, host->h_length );
	socket_address.sin_family = host->h_addrtype;
	socket_address.sin_port = htons( port );

	/* get the actual socket handle */
	hsocket = socket( host->h_addrtype, SOCK_STREAM, 0 );
	if ( hsocket < 0 )        /* we couldn't grab the socket */
		return NULL;

	if ( connect( hsocket, ( struct sockaddr* ) & socket_address,
					              sizeof( socket_address ) ) < 0 )
					return NULL;

	return fdopen( hsocket, "r+" );          /* we made it */
}


int get_subdirs( const char *path, char **buffer )
{
	int hits = 0, status;
	char *subdirname;
	DIR *dirp;
	struct dirent *dir_contents;
	struct stat file_stats;

	if ( path == NULL || ( dirp = opendir( path ) ) == NULL ) {
		return hits;
	}

	while ( ( dir_contents = readdir( dirp ) ) != NULL ) {
		if ( strcmp( dir_contents->d_name, "." ) != 0 &&
			        strcmp( dir_contents->d_name, ".." ) != 0 ) {

			subdirname = ( char* ) malloc( strlen( path ) + strlen( dir_contents->d_name ) + 2 );
			sprintf( subdirname, "%s/%s", path, dir_contents->d_name );
			status = stat( subdirname, &file_stats );

			if ( status == 0 && hits < 1000 ) {
				if ( S_ISDIR( file_stats.st_mode ) ) { /* we are a subdir */
					buffer[ hits ] = strdup( subdirname );
					hits++;
				}
			}
			free( subdirname );
		}
	}

	return hits;
}

char* int2str( int integer )
{
	char * newstr = ( char* ) malloc( 10 );
	sprintf( newstr, "%d", integer );
	return newstr;
}

long check_free_space(char* dir) 
{
	FILE *file;
	long len;
	char cmd[MAX_FILE_NAME_LENGTH];
	char buffer[MAX_FILE_NAME_LENGTH];

	snprintf(cmd, MAX_FILE_NAME_LENGTH - 1, "df -P '%s' 2>/dev/null | awk '{print $4}' | tail -1", dir);
	file = popen(cmd, "r");
	//fscanf(file, "%s\n", &buffer); /* read the Available title */
	fscanf(file, "%ld", &len); /* now read the length */
	pclose(file);
	return len;
}

int check_dir(char *dir) {
	int rc;
	struct stat ds;

	rc = access(dir, F_OK);
	if (rc != 0) {
		return MISC_DOES_NOT_EXISTS;
	}
	rc = stat(dir, &ds);
	if (rc != 0) {
		return MISC_NOT_DIR;
	} 
	if (!S_ISDIR(ds.st_mode)) {
		return MISC_NOT_DIR;
	}
	rc = access(dir, X_OK | W_OK | R_OK);
	if (rc != 0) {
		return MISC_NOT_WRITABLE;
	}
	return MISC_OK;
}

int create_dir(char *path) {
	char *parent;
	char *tmp;
	int rc;

	tmp = strdup(path); 	/* preserve our string */
	parent = dirname(tmp);
	rc = check_dir(parent);
	if (strcmp(parent, "/") == 0) {
		return -1; 	/* stop endless loop */
	}
	if (rc == MISC_NOT_DIR || rc == MISC_NOT_WRITABLE) { 
		return -1;
	} else if (rc == MISC_DOES_NOT_EXISTS) {
		/* recursively create */
		if (create_dir(parent) != 0) {
			fprintf(stderr, "failed to create %s\n", parent);
			return -1;
		}
	}
	rc = mkdir(path, 0755);
	free(tmp);
	return rc;
}

int is_found(char *plugin) {
    FILE *pf;
    char buffer[MAX_COMMAND_LENGTH];
    char cmd[MAX_COMMAND_LENGTH];

    snprintf(cmd, sizeof(cmd) -1, "%s 2>&1", plugin);

    pf = popen(cmd, "r");
    if (pf == NULL) {
        return 0;
    }
    fgets(buffer, sizeof(buffer) - 1, pf);
    pclose(pf);
    if (strncmp(buffer, "sh:", 3) != 0) {
        return 1;
    }
    return 0;
}

static char *wd, *ed;
static char *wfext, *ecfext;

void create_filenames_from_format(_main_data *main_data)
{
	int i;
	static char *df;

	i = strlen(config.wav_path) - 1;
	if (i >= 0 && config.wav_path[i] == '/')
		config.wav_path[i] = 0;
	i = strlen(config.mp3_path) - 1;
	if (i >= 0 && config.mp3_path[i] == '/')
		config.mp3_path[i] = 0;
	if (config.cddb_config.make_directories && main_data->disc_title[0])
	{
		parse_rx_format_string( &df,
				config.cddb_config.dir_format_string, -1, NULL, NULL, 
				main_data->disc_artist, main_data->disc_title, NULL );

		remove_non_unix_chars( df );

		mk_strcat(&wd, config.wav_path, "/", df, "/", NULL);
		mk_strcat(&ed, config.mp3_path, "/", df, "/", NULL);

		create_dir(wd);
		create_dir(ed);
	} else {
		mk_strcat(&wd, config.wav_path, "/", NULL);
		mk_strcat(&ed, config.mp3_path, "/", NULL);
	}

	if ( config.auto_append_extension == TRUE ) {
		wfext = ".wav";
		if ( strcmp( config.encoder.encoder, "oggenc" ) == 0)
			ecfext = ".ogg";
		else if (strcmp( config.encoder.encoder, "flac" ) == 0)
			ecfext = ".flac";
		else
			ecfext = ".mp3";
	}
	else
		wfext = ecfext = "";
}

void create_file_names_for_track(_main_data *main_data, int track, char **wfp, char **efp)
{
	static char *buffer;
	int rc;
	

	rc = parse_rx_format_string(&buffer,
		config.cddb_config.format_string, track, NULL, NULL, 
		main_data->disc_artist, main_data->disc_title, 
		main_data->track[ track ].title );
	if ( rc < 0 ) {
		err_handler( RX_PARSING_ERR, "Check if the format string contains\n"
					 "format characters other than %a %# %v or %s" );
	}

	remove_non_unix_chars( buffer );
	convert_slashes(buffer, '-');	 
	if ( config.cddb_config.convert_spaces == TRUE ) {
		convert_spaces( buffer, '_' );
	}

	if (wfp)
		mk_strcat(wfp, wd, buffer, wfext, NULL);
	if (efp)
		mk_strcat(efp, ed, buffer, ecfext, NULL);
}

/*
** get_track_title - Copy track artist & title for specified track from
**                   main_data to a specified location in freedb format.
*/
void get_track_title(char *dest, _main_data *main_data, int tno)
{
	struct _track *tk_p = &(main_data->track[tno]);
	if (tk_p->artist)
		sprintf(dest, "%s / %s", tk_p->artist, tk_p->title);
	else
		strncpy(dest, tk_p->title, MAX_FILE_NAME_LENGTH);
}

/*
** put_track_title - Put track artist & title in freedb format into
**                   main_data for specified track number.
** NOTE: A track artist can be specified
*/
void put_track_title(char *src, _main_data *main_data, int tno)
{
	struct _track *tk_p = &(main_data->track[tno]);
	char *sp = src, *cp, *ep;
	int c;

	/* Strip leading blanks */
	while ( isspace(*sp) )
		++sp;

	/* Split off track artist if specified */
	if (cp = rindex(sp, '/')) {
		for (ep = cp - 1; ep > sp && isspace(*ep); --ep)
			;
		c = *(++ep);
		*ep = 0;
		mk_str( &(tk_p->artist), sp );
		*ep = c;
		for (sp = cp + 1; isspace(*sp); ++sp)
			;
	}
	else {
		mk_buf( &(tk_p->artist), 0);
	}

	/* Strip trailing spaces */
	for (ep = sp + strlen(sp) - 1; isspace(*ep); --ep)
		;

	memcpy(tk_p->title, sp, ep - sp + 1);
	tk_p->title[ep - sp + 1] = 0;
}

/* dup_str - return a copy of a string in allocated memory */
char *dup_str(char *inp)
{
	char *out;
	int len;
	if ( !inp || (len = strlen(inp)) == 0 )
		return (char *)0;
	out = malloc(len + 1);
	return strcpy(out, inp);
}

/*
** mk_buf - allocate a buffer of a specified size & save the pointer.
**         If the pointer was non-null, free the storage previously
**         pointed to.
*/
void mk_buf(char **ptr, int size)
{
	if (*ptr) {
		free(*ptr);
		*ptr = NULL;
	}
	if (size > 0)
		if ( !(*ptr = malloc(size)) ) {
			fprintf(stderr, "Unable to allocate %d bytes of memory.. exiting\n", size);
			exit(3);
		}
}

/*
** mk_str - Make a copy of a string in allocated storage saving a pointer in ptr.
**          If *ptr was non-null, free previously allocated storage.
*/
void mk_str(char **ptr, char *inp)
{
	int n = strlen(inp);
	mk_buf(ptr, n + 1);
	if (n > 0)
		strcpy(*ptr, inp);
}

/*
** mk_strcat - Make a new string by concatenating the specified strings,
**             in order, & storing the result in allocated storage.  A
**             pointer to the result will be stored in *ptr.  The last
**			   argument must be a null pointer, marking the end of the
**             list of strings to be concatenated.
**             If *ptr is non-null, free previously allocated storage.
*/
void mk_strcat(char **ptr, ...)
{
	va_list ap;
	char *cp, *cpb;
	int tlen = 0;

	va_start(ap, ptr);
	while (cp = va_arg(ap, char *))
		tlen += strlen(cp);
	va_end(ap);
	mk_buf(ptr, tlen + 1);
	va_start(ap, ptr);
	for (cpb = *ptr; cp = va_arg(ap, char *); cpb += strlen(cp))
		strcpy(cpb, cp);
	va_end(ap);
}
