Blame src/metaprint.c

Packit c32a2d
/*
Packit c32a2d
	id3print: display routines for ID3 tags (including filtering of UTF8 to ASCII)
Packit c32a2d
Packit c32a2d
	copyright 2006-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
Packit c32a2d
	see COPYING and AUTHORS files in distribution or http://mpg123.org
Packit c32a2d
	initially written by Thomas Orgis
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
/* Need snprintf(). */
Packit c32a2d
#define _DEFAULT_SOURCE
Packit c32a2d
#define _BSD_SOURCE
Packit c32a2d
#include "mpg123app.h"
Packit c32a2d
#include "common.h"
Packit c32a2d
#include "genre.h"
Packit c32a2d
#include "debug.h"
Packit c32a2d
Packit c32a2d
static const char joker_symbol = '*';
Packit c32a2d
Packit c32a2d
/* Metadata name field texts with index enumeration. */
Packit c32a2d
enum tagcode { TITLE=0, ARTIST, ALBUM, COMMENT, YEAR, GENRE, FIELDS };
Packit c32a2d
static const char* name[FIELDS] =
Packit c32a2d
{
Packit c32a2d
	"Title"
Packit c32a2d
,	"Artist"
Packit c32a2d
,	"Album"
Packit c32a2d
,	"Comment"
Packit c32a2d
,	"Year"
Packit c32a2d
,	"Genre"
Packit c32a2d
};
Packit c32a2d
Packit c32a2d
/* Two-column printing: max length of left and right name strings.
Packit c32a2d
   see print_id3 for what goes left or right.
Packit c32a2d
   Choose namelen[0] >= namelen[1]! */
Packit c32a2d
static const int namelen[2] = {7, 6};
Packit c32a2d
/* Overhead is Name + ": " and also plus "  " for right column. */
Packit c32a2d
/* pedantic C89 does not like:
Packit c32a2d
const int overhead[2] = { namelen[0]+2, namelen[1]+4 }; */
Packit c32a2d
static const int overhead[2] = { 9, 10 };
Packit c32a2d
Packit c32a2d
static void utf8_ascii(mpg123_string *dest, mpg123_string *source);
Packit c32a2d
/* Copy UTF-8 string or melt it down to ASCII, also returning the character length. */
Packit c32a2d
static size_t transform(mpg123_string *dest, mpg123_string *source)
Packit c32a2d
{
Packit c32a2d
	debug("transform!");
Packit c32a2d
	if(source == NULL) return 0;
Packit c32a2d
Packit c32a2d
	if(utf8env) mpg123_copy_string(source, dest);
Packit c32a2d
	else        utf8_ascii(dest, source);
Packit c32a2d
Packit c32a2d
	return mpg123_strlen(dest, utf8env);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static void id3_gap(mpg123_string *dest, size_t count, char *v1, size_t *len)
Packit c32a2d
{
Packit c32a2d
	if(!dest->fill)
Packit c32a2d
	{
Packit c32a2d
		if(dest->size >= count+1 || mpg123_resize_string(dest, count+1))
Packit c32a2d
		{
Packit c32a2d
			strncpy(dest->p,v1,count);
Packit c32a2d
			dest->p[count] = 0;
Packit c32a2d
			*len = strlen(dest->p);
Packit c32a2d
			dest->fill = *len + 1;
Packit c32a2d
			/* We have no idea what encoding this is.
Packit c32a2d
			   So, to prevent mess up of our UTF-8 display, filter anything above ASCII.
Packit c32a2d
			   But in non-UTF-8 mode, we pray that the verbatim contents are meaningful to the user. Might filter non-printable characters, though. */
Packit c32a2d
			if(utf8env)
Packit c32a2d
			{
Packit c32a2d
				size_t i;
Packit c32a2d
				for(i=0; i<dest->fill-1; ++i)
Packit c32a2d
				if(dest->p[i] & 0x80) dest->p[i] = joker_symbol;
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Print one metadata entry on a line, aligning the beginning. */
Packit c32a2d
static void print_oneline( FILE* out
Packit c32a2d
,	const mpg123_string *tag, enum tagcode fi, int long_mode )
Packit c32a2d
{
Packit c32a2d
	char fmt[14]; /* "%s:%-XXXs%s\n" plus one null */
Packit c32a2d
	if(!tag[fi].fill && !long_mode)
Packit c32a2d
		return;
Packit c32a2d
Packit c32a2d
	if(long_mode)
Packit c32a2d
		fprintf(out, "\t");
Packit c32a2d
	snprintf( fmt, sizeof(fmt)-1, "%%s:%%-%ds%%s\n"
Packit c32a2d
	,	1+namelen[0]-(int)strlen(name[fi]) );
Packit c32a2d
	fprintf(out, fmt, name[fi], " ", tag[fi].fill ? tag[fi].p : "");
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
	Print a pair of tag name-value pairs along each other in two columns or
Packit c32a2d
	each on a line if that is not sensible.
Packit c32a2d
	This takes a given length (in columns) into account, not just bytes.
Packit c32a2d
	If that length would be computed taking grapheme clusters into account, things
Packit c32a2d
	could be fine for the whole world of Unicode. So far we ride only on counting
Packit c32a2d
	possibly multibyte characters (unless mpg123_strlen() got adapted meanwhile).
Packit c32a2d
*/
Packit c32a2d
static void print_pair
Packit c32a2d
(
Packit c32a2d
	FILE* out /* Output stream. */
Packit c32a2d
,	const int *climit /* Maximum width of columns (two values). */
Packit c32a2d
,	const mpg123_string *tag /* array of tag value strings */
Packit c32a2d
,	const size_t *len /* array of character/column lengths */
Packit c32a2d
,	enum tagcode f0, enum tagcode f1 /* field indices for column 0 and 1 */
Packit c32a2d
){
Packit c32a2d
	/* Two-column printout if things match, dumb printout otherwise. */
Packit c32a2d
	if(  tag[f0].fill         && tag[f1].fill
Packit c32a2d
	  && len[f0] <= (size_t)climit[0] && len[f1] <= (size_t)climit[1] )
Packit c32a2d
	{
Packit c32a2d
		char cfmt[35]; /* "%s:%-XXXs%-XXXs  %s:%-XXXs%-XXXs\n" plus one extra null from snprintf */
Packit c32a2d
		int chardiff[2];
Packit c32a2d
		size_t bytelen;
Packit c32a2d
Packit c32a2d
		/* difference between character length and byte length */
Packit c32a2d
		bytelen = strlen(tag[f0].p);
Packit c32a2d
		chardiff[0] = len[f0] < bytelen ? bytelen-len[f0] : 0;
Packit c32a2d
		bytelen = strlen(tag[f1].p);
Packit c32a2d
		chardiff[1] = len[f1] < bytelen ? bytelen-len[f1] : 0;
Packit c32a2d
Packit c32a2d
		/* Two-column format string with added padding for multibyte chars. */
Packit c32a2d
		snprintf( cfmt, sizeof(cfmt)-1, "%%s:%%-%ds%%-%ds  %%s:%%-%ds%%-%ds\n"
Packit c32a2d
		,	1+namelen[0]-(int)strlen(name[f0]), climit[0]+chardiff[0]
Packit c32a2d
		,	1+namelen[1]-(int)strlen(name[f1]), climit[1]+chardiff[1] );
Packit c32a2d
		/* Actual printout of name and value pairs. */
Packit c32a2d
		fprintf(out, cfmt, name[f0], " ", tag[f0].p, name[f1], " ", tag[f1].p);
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		print_oneline(out, tag, f0, FALSE);
Packit c32a2d
		print_oneline(out, tag, f1, FALSE);
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Print tags... limiting the UTF-8 to ASCII, if necessary. */
Packit c32a2d
void print_id3_tag(mpg123_handle *mh, int long_id3, FILE *out)
Packit c32a2d
{
Packit c32a2d
	char genre_from_v1 = 0;
Packit c32a2d
	enum tagcode ti;
Packit c32a2d
	mpg123_string tag[FIELDS];
Packit c32a2d
	size_t len[FIELDS];
Packit c32a2d
	mpg123_id3v1 *v1;
Packit c32a2d
	mpg123_id3v2 *v2;
Packit c32a2d
	/* no memory allocated here, so return is safe */
Packit c32a2d
	for(ti=0; ti
Packit c32a2d
	/* extract the data */
Packit c32a2d
	mpg123_id3(mh, &v1, &v2;;
Packit c32a2d
	/* Only work if something there... */
Packit c32a2d
	if(v1 == NULL && v2 == NULL) return;
Packit c32a2d
	if(v2 != NULL) /* fill from ID3v2 data */
Packit c32a2d
	{
Packit c32a2d
		len[TITLE]   = transform(&tag[TITLE],   v2->title);
Packit c32a2d
		len[ARTIST]  = transform(&tag[ARTIST],  v2->artist);
Packit c32a2d
		len[ALBUM]   = transform(&tag[ALBUM],   v2->album);
Packit c32a2d
		len[COMMENT] = transform(&tag[COMMENT], v2->comment);
Packit c32a2d
		len[YEAR]    = transform(&tag[YEAR],    v2->year);
Packit c32a2d
		len[GENRE]   = transform(&tag[GENRE],   v2->genre);
Packit c32a2d
	}
Packit c32a2d
	if(v1 != NULL) /* fill gaps with ID3v1 data */
Packit c32a2d
	{
Packit c32a2d
		/* I _could_ skip the recalculation of fill ... */
Packit c32a2d
		id3_gap(&tag[TITLE],   30, v1->title,   &len[TITLE]);
Packit c32a2d
		id3_gap(&tag[ARTIST],  30, v1->artist,  &len[ARTIST]);
Packit c32a2d
		id3_gap(&tag[ALBUM],   30, v1->album,   &len[ALBUM]);
Packit c32a2d
		id3_gap(&tag[COMMENT], 30, v1->comment, &len[COMMENT]);
Packit c32a2d
		id3_gap(&tag[YEAR],    4,  v1->year,    &len[YEAR]);
Packit c32a2d
		/*
Packit c32a2d
			genre is special... v1->genre holds an index, id3v2 genre may contain indices in textual form and raw textual genres...
Packit c32a2d
		*/
Packit c32a2d
		if(!tag[GENRE].fill)
Packit c32a2d
		{
Packit c32a2d
			if(tag[GENRE].size >= 31 || mpg123_resize_string(&tag[GENRE],31))
Packit c32a2d
			{
Packit c32a2d
				if(v1->genre <= genre_count)
Packit c32a2d
				{
Packit c32a2d
					strncpy(tag[GENRE].p, genre_table[v1->genre], 30);
Packit c32a2d
				}
Packit c32a2d
				else
Packit c32a2d
				{
Packit c32a2d
					strncpy(tag[GENRE].p,"Unknown",30);
Packit c32a2d
				}
Packit c32a2d
				tag[GENRE].p[30] = 0;
Packit c32a2d
				/* V1 was plain ASCII, so strlen is fine. */
Packit c32a2d
				len[GENRE] = strlen(tag[GENRE].p);
Packit c32a2d
				tag[GENRE].fill = len[GENRE] + 1;
Packit c32a2d
				genre_from_v1 = 1;
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(tag[GENRE].fill && !genre_from_v1)
Packit c32a2d
	{
Packit c32a2d
		/*
Packit c32a2d
			id3v2.3 says (id)(id)blabla and in case you want ot have (blabla) write ((blabla)
Packit c32a2d
			also, there is
Packit c32a2d
			(RX) Remix
Packit c32a2d
			(CR) Cover
Packit c32a2d
			id3v2.4 says
Packit c32a2d
			"one or several of the ID3v1 types as numerical strings"
Packit c32a2d
			or define your own (write strings), RX and CR 
Packit c32a2d
Packit c32a2d
			Now I am very sure that I'll encounter hellishly mixed up id3v2 frames, so try to parse both at once.
Packit c32a2d
		*/
Packit c32a2d
		mpg123_string tmp;
Packit c32a2d
		mpg123_init_string(&tmp);
Packit c32a2d
		debug1("interpreting genre: %s\n", tag[GENRE].p);
Packit c32a2d
		if(mpg123_copy_string(&tag[GENRE], &tmp))
Packit c32a2d
		{
Packit c32a2d
			size_t num = 0;
Packit c32a2d
			size_t nonum = 0;
Packit c32a2d
			size_t i;
Packit c32a2d
			enum { nothing, number, outtahere } state = nothing;
Packit c32a2d
			tag[GENRE].fill = 0; /* going to be refilled */
Packit c32a2d
			/* number\n -> id3v1 genre */
Packit c32a2d
			/* (number) -> id3v1 genre */
Packit c32a2d
			/* (( -> ( */
Packit c32a2d
			for(i = 0; i < tmp.fill; ++i)
Packit c32a2d
			{
Packit c32a2d
				debug1("i=%lu", (unsigned long) i);
Packit c32a2d
				switch(state)
Packit c32a2d
				{
Packit c32a2d
					case nothing:
Packit c32a2d
						nonum = i;
Packit c32a2d
						if(tmp.p[i] == '(')
Packit c32a2d
						{
Packit c32a2d
							num = i+1; /* number starting as next? */
Packit c32a2d
							state = number;
Packit c32a2d
							debug1("( before number at %lu?", (unsigned long) num);
Packit c32a2d
						}
Packit c32a2d
						/* you know an encoding where this doesn't work? */
Packit c32a2d
						else if(tmp.p[i] >= '0' && tmp.p[i] <= '9')
Packit c32a2d
						{
Packit c32a2d
							num = i;
Packit c32a2d
							state = number;
Packit c32a2d
							debug1("direct number at %lu", (unsigned long) num);
Packit c32a2d
						}
Packit c32a2d
						else state = outtahere;
Packit c32a2d
					break;
Packit c32a2d
					case number:
Packit c32a2d
						/* fake number alert: (( -> ( */
Packit c32a2d
						if(tmp.p[i] == '(')
Packit c32a2d
						{
Packit c32a2d
							nonum = i;
Packit c32a2d
							state = outtahere;
Packit c32a2d
							debug("no, it was ((");
Packit c32a2d
						}
Packit c32a2d
						else if(tmp.p[i] == ')' || tmp.p[i] == '\n' || tmp.p[i] == 0)
Packit c32a2d
						{
Packit c32a2d
							if(i-num > 0)
Packit c32a2d
							{
Packit c32a2d
								/* we really have a number */
Packit c32a2d
								int gid;
Packit c32a2d
								char* genre = "Unknown";
Packit c32a2d
								tmp.p[i] = 0;
Packit c32a2d
								gid = atoi(tmp.p+num);
Packit c32a2d
Packit c32a2d
								/* get that genre */
Packit c32a2d
								if(gid >= 0 && gid <= genre_count) genre = genre_table[gid];
Packit c32a2d
								debug1("found genre: %s", genre);
Packit c32a2d
Packit c32a2d
								if(tag[GENRE].fill) mpg123_add_string(&tag[GENRE], ", ");
Packit c32a2d
								mpg123_add_string(&tag[GENRE], genre);
Packit c32a2d
								nonum = i+1; /* next possible stuff */
Packit c32a2d
								state = nothing;
Packit c32a2d
								debug1("had a number: %i", gid);
Packit c32a2d
							}
Packit c32a2d
							else
Packit c32a2d
							{
Packit c32a2d
								/* wasn't a number, nonum is set */
Packit c32a2d
								state = outtahere;
Packit c32a2d
								debug("no (num) thing...");
Packit c32a2d
							}
Packit c32a2d
						}
Packit c32a2d
						else if(!(tmp.p[i] >= '0' && tmp.p[i] <= '9'))
Packit c32a2d
						{
Packit c32a2d
							/* no number at last... */
Packit c32a2d
							state = outtahere;
Packit c32a2d
							debug("nothing numeric here");
Packit c32a2d
						}
Packit c32a2d
						else
Packit c32a2d
						{
Packit c32a2d
							debug("still number...");
Packit c32a2d
						}
Packit c32a2d
					break;
Packit c32a2d
					default: break;
Packit c32a2d
				}
Packit c32a2d
				if(state == outtahere) break;
Packit c32a2d
			}
Packit c32a2d
			/* Small hack: Avoid repeating genre in case of stuff like
Packit c32a2d
			   (144)Thrash Metal being given. The simple cases. */
Packit c32a2d
			if(
Packit c32a2d
				nonum < tmp.fill-1 &&
Packit c32a2d
				(!tag[GENRE].fill || strncmp(tag[GENRE].p, tmp.p+nonum, tag[GENRE].fill))
Packit c32a2d
			)
Packit c32a2d
			{
Packit c32a2d
				if(tag[GENRE].fill) mpg123_add_string(&tag[GENRE], ", ");
Packit c32a2d
				mpg123_add_string(&tag[GENRE], tmp.p+nonum);
Packit c32a2d
			}
Packit c32a2d
			/* Do not like that ... assumes plain ASCII ... */
Packit c32a2d
			len[GENRE] = strlen(tag[GENRE].p);
Packit c32a2d
		}
Packit c32a2d
		mpg123_free_string(&tmp);
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(long_id3)
Packit c32a2d
	{
Packit c32a2d
		fprintf(out,"\n");
Packit c32a2d
		/* print id3v2 */
Packit c32a2d
		print_oneline(out, tag, TITLE,   TRUE);
Packit c32a2d
		print_oneline(out, tag, ARTIST,  TRUE);
Packit c32a2d
		print_oneline(out, tag, ALBUM,   TRUE);
Packit c32a2d
		print_oneline(out, tag, YEAR,    TRUE);
Packit c32a2d
		print_oneline(out, tag, GENRE,   TRUE);
Packit c32a2d
		print_oneline(out, tag, COMMENT, TRUE);
Packit c32a2d
		fprintf(out,"\n");
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		/* We are trying to be smart here and conserve some vertical space.
Packit c32a2d
		   So we will skip tags not set, and try to show them in two parallel
Packit c32a2d
		   columns if they are short, which is by far the most common case. */
Packit c32a2d
		int linelimit;
Packit c32a2d
		int climit[2];
Packit c32a2d
Packit c32a2d
		/* Adapt formatting width to terminal if possible. */
Packit c32a2d
		linelimit = term_width(fileno(out));
Packit c32a2d
		if(linelimit < 0)
Packit c32a2d
			linelimit=overhead[0]+30+overhead[1]+30; /* the old style, based on ID3v1 */
Packit c32a2d
		if(linelimit > 200)
Packit c32a2d
			linelimit = 200; /* Not too wide. Also for format string safety. */
Packit c32a2d
		/* Divide the space between the two columns, not wasting any. */
Packit c32a2d
		climit[1] = linelimit/2-overhead[0];
Packit c32a2d
		climit[0] = linelimit-linelimit/2-overhead[1];
Packit c32a2d
		debug3("linelimits: %i  < %i | %i >", linelimit, climit[0], climit[1]);
Packit c32a2d
Packit c32a2d
		if(climit[0] <= 0 || climit[1] <= 0)
Packit c32a2d
		{
Packit c32a2d
			/* Ensure disabled column printing, no play with signedness in comparisons. */
Packit c32a2d
			climit[0] = 0;
Packit c32a2d
			climit[1] = 0;
Packit c32a2d
		}
Packit c32a2d
		fprintf(out,"\n"); /* Still use one separator line. Too ugly without. */
Packit c32a2d
		print_pair(out, climit, tag, len, TITLE,   ARTIST);
Packit c32a2d
		print_pair(out, climit, tag, len, COMMENT, ALBUM );
Packit c32a2d
		print_pair(out, climit, tag, len, YEAR,    GENRE );
Packit c32a2d
	}
Packit c32a2d
	for(ti=0; ti
Packit c32a2d
Packit c32a2d
	if(v2 != NULL && APPFLAG(MPG123APP_LYRICS))
Packit c32a2d
	{
Packit c32a2d
		/* find and print texts that have USLT IDs */
Packit c32a2d
		size_t i;
Packit c32a2d
		for(i=0; i<v2->texts; ++i)
Packit c32a2d
		{
Packit c32a2d
			if(!memcmp(v2->text[i].id, "USLT", 4))
Packit c32a2d
			{
Packit c32a2d
				/* split into lines, ensure usage of proper local line end */
Packit c32a2d
				size_t a=0;
Packit c32a2d
				size_t b=0;
Packit c32a2d
				char lang[4]; /* just a 3-letter ASCII code, no fancy encoding */
Packit c32a2d
				mpg123_string innline;
Packit c32a2d
				mpg123_string outline;
Packit c32a2d
				mpg123_string *uslt = &v2->text[i].text;
Packit c32a2d
Packit c32a2d
				memcpy(lang, &v2->text[i].lang, 3);
Packit c32a2d
				lang[3] = 0;
Packit c32a2d
				printf("Lyrics begin, language: %s; %s\n\n", lang,  v2->text[i].description.fill ? v2->text[i].description.p : "");
Packit c32a2d
Packit c32a2d
				mpg123_init_string(&innline);
Packit c32a2d
				mpg123_init_string(&outline);
Packit c32a2d
				while(a < uslt->fill)
Packit c32a2d
				{
Packit c32a2d
					b = a;
Packit c32a2d
					while(b < uslt->fill && uslt->p[b] != '\n' && uslt->p[b] != '\r') ++b;
Packit c32a2d
					/* Either found end of a line or end of the string (null byte) */
Packit c32a2d
					mpg123_set_substring(&innline, uslt->p, a, b-a);
Packit c32a2d
					transform(&outline, &innline);
Packit c32a2d
					printf(" %s\n", outline.p);
Packit c32a2d
Packit c32a2d
					if(uslt->p[b] == uslt->fill) break; /* nothing more */
Packit c32a2d
Packit c32a2d
					/* Swallow CRLF */
Packit c32a2d
					if(uslt->fill-b > 1 && uslt->p[b] == '\r' && uslt->p[b+1] == '\n') ++b;
Packit c32a2d
Packit c32a2d
					a = b + 1; /* next line beginning */
Packit c32a2d
				}
Packit c32a2d
				mpg123_free_string(&innline);
Packit c32a2d
				mpg123_free_string(&outline);
Packit c32a2d
Packit c32a2d
				printf("\nLyrics end.\n");
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void print_icy(mpg123_handle *mh, FILE *outstream)
Packit c32a2d
{
Packit c32a2d
	char* icy;
Packit c32a2d
	if(MPG123_OK == mpg123_icy(mh, &icy))
Packit c32a2d
	{
Packit c32a2d
		mpg123_string in;
Packit c32a2d
		mpg123_init_string(&in);
Packit c32a2d
		if(mpg123_store_utf8(&in, mpg123_text_icy, (unsigned char*)icy, strlen(icy)+1))
Packit c32a2d
		{
Packit c32a2d
			mpg123_string out;
Packit c32a2d
			mpg123_init_string(&out;;
Packit c32a2d
Packit c32a2d
			transform(&out, &in);
Packit c32a2d
			if(out.fill)
Packit c32a2d
			fprintf(outstream, "\nICY-META: %s\n", out.p);
Packit c32a2d
Packit c32a2d
			mpg123_free_string(&out;;
Packit c32a2d
		}
Packit c32a2d
		mpg123_free_string(&in);
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static void utf8_ascii(mpg123_string *dest, mpg123_string *source)
Packit c32a2d
{
Packit c32a2d
	size_t spos = 0;
Packit c32a2d
	size_t dlen = 0;
Packit c32a2d
	char *p;
Packit c32a2d
Packit c32a2d
	/* Find length of ASCII string (count non-continuation bytes).
Packit c32a2d
	   Do _not_ change this to mpg123_strlen()!
Packit c32a2d
	   It needs to match the loop below. Especially dlen should not stop at embedded null bytes. You can get any trash from ID3! */
Packit c32a2d
	for(spos=0; spos < source->fill; ++spos)
Packit c32a2d
	if((source->p[spos] & 0xc0) == 0x80) continue;
Packit c32a2d
	else ++dlen;
Packit c32a2d
Packit c32a2d
	/* The trailing zero is included in dlen; if there is none, one character will be cut. Bad input -> bad output. */
Packit c32a2d
	if(!mpg123_resize_string(dest, dlen)){ mpg123_free_string(dest); return; }
Packit c32a2d
	/* Just ASCII, we take it easy. */
Packit c32a2d
	p = dest->p;
Packit c32a2d
Packit c32a2d
	for(spos=0; spos < source->fill; ++spos)
Packit c32a2d
	{
Packit c32a2d
		/* UTF-8 continuation byte 0x10?????? */
Packit c32a2d
		if((source->p[spos] & 0xc0) == 0x80) continue;
Packit c32a2d
		/* UTF-8 lead byte 0x11?????? */
Packit c32a2d
		else if(source->p[spos] & 0x80) *p = joker_symbol;
Packit c32a2d
		/* just ASCII, 0x0??????? */
Packit c32a2d
		else *p = source->p[spos];
Packit c32a2d
Packit c32a2d
		++p; /* next output char */
Packit c32a2d
	}
Packit c32a2d
	/* Always close the string, trailing zero might be missing. */
Packit c32a2d
	if(dest->size) dest->p[dest->size-1] = 0;
Packit c32a2d
Packit c32a2d
	dest->fill = dest->size;
Packit c32a2d
}