summary refs log tree commit diff stats
path: root/plugins/mpcinfo/mp3Info.c
blob: b7112a5d3e7f6b6fe8e282081b4595a6e0d6fc53 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/*
 *  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

//#include <stdio.h>
#include <sys/stat.h>
//#include "functions.c"

struct tagInfo{
	int mode;
	int cbr;
	int bitrate;
	unsigned int freq;
	char *artist;
	char *title;
	char *album;
	char *comment;
	char *genre;
	//int genre;
	//int track;
};

static int RATES[2][3][15]={
				{//mpeg2
                    {-1,8,16,24,32,64,80,56,64,128,160,112,128,256,320},//layer3 (V2)    
					{-1,32,48,56,64,80,96,112,128,160,192,224,256,320,384},//layer2 (V2)
					{-1,32,64,96,128,160,192,224,256,288,320,352,384,416,448},//layer1 (V2)
                },
                {//mpeg1
					{-1,32,40,48,56,64,80,96,112,128,160,192,224,256,320},//layer3 (V1)
					{-1,32,48,56,64,80,96,112,128,160,192,224,256,320,384},//layer2 (V1)
                    {-1,32,64,96,128,160,192,224,256,288,320,352,384,416,448},//layer1 (V1)
        }};
static int FREQS[2][4]={{22050,24000,16000,-1},{44100,48000,32000,-1}};
//static double FRATES[]={38.5,32.5,27.8,0.0};

static char GENRES[][50]={"Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge","Hip-Hop","Jazz","Metal",
"New Age","Oldies","Other","Pop","R&B","Rap","Reggae","Rock","Techno","Industrial",
"Alternative","Ska","Death Metal","Pranks","Soundtrack","Euro-Techno","Ambient","Trip-Hop","Vocal","Jazz+Funk",
"Fusion","Trance","Classical","Instrumental","Acid","House","Game","Sound Clip","Gospel","Noise",
"AlternRock","Bass","Soul","Punk","Space","Meditative","Instrumental Pop","Instrumental Rock","Ethnic","Gothic",
"Darkwave","Techno-Industrial","Electronic","Pop-Folk","Eurodance","Dream","Southern Rock","Comedy","Cult","Gangsta",
"Top 40","Christian Rap","Pop/Funk","Jungle","Native American","Cabaret","New Wave","Psychadelic","Rave","Showtunes",
"Trailer","Lo-Fi","Tribal","Acid Punk","Acid Jazz","Polka","Retro","Musical","Rock & Roll","Hard Rock",

//################## END OF OFFICIAL ID3 TAGS, WINAMP TAGS BELOW ########################################

"Folk","Folk/Rock","National Folk","Swing","Fast Fusion","Bebob","Latin","Revival","Celtic","Bluegrass",
"Avantgarde","Gothic Rock","Progressive Rock","Psychedelic Rock","Symphonic Rock","Slow Rock","Big Band","Chorus","Easy Listening","Acoustic",
"Humour","Speech","Chanson","Opera","Chamber Music","Sonata","Symphony","Booty Bass","Primus","Porn Groove",
"Satire","Slow Jam","Club","Tango","Samba","Folklore","Ballad","Poweer Ballad","Rhytmic Soul","Freestyle",
"Duet","Punk Rock","Drum Solo","A Capela","Euro-House","Dance Hall",

//################## FOUND AT http://en.wikipedia.org/wiki/ID3 ###########################################

"Goa","Drum & Bass","Club-House","Hardcore",
"Terror","Indie","BritPop","Negerpunk","Polsk Punk","Beat","Christian Gangsta Rap","Heavy Metal","Black Metal","Crossover",
"Contemporary Christian","Christian Rock","Merengue","Salsa","Thrash Metal","Anime","JPop","Synthpop"

};

static char MODES [][13]={"Stereo","Joint-Stereo","Dual-Channel","Mono"};

int iPow(int x, int y){return (int)(pow((double)x,(double) y));}

int str2int(char *text){
    //if (DEBUG==1) putlog("converting string to int");
    int i;
    int ret=0;
    for (i=1;i<=strlen(text);i++){
        if ((text[strlen(text)-i]>57)||(text[strlen(text)-i]<48)){
           hexchat_printf(ph,"invalid char in string: %i",text[strlen(text)-i]);
           return 255;
        }
        ret+=((int)text[strlen(text)-i]-48)*iPow(10,i-1);
    }
    //hexchat_printf(ph, "str2int(%s)=%i",text,ret);
    //if (DEBUG==1) putlog("int converted");
    return ret;
}
/*
static int getSize(char *file){
    //if (DEBUG==1) putlog("reading filesize");
	struct stat info;
	if (stat(file,&info)!=0) return -1;
	return info.st_size;
}*/
/*
int inStr(char *s1, int sl1, char *s2){
    //if (DEBUG==1) putlog("checking instr");
	int i;int j;
	for(i=0;i<sl1-strlen(s2);i++){
		for (j=0;j<strlen(s2);j++){
			if (s1[i+j]!=s2[j]) j=strlen(s2)+2;
		}
		if (j==strlen(s2)) return i;
	}
	return -1;
}

static char *subString(char *text, int first, int length, int spcKill){
//if (DEBUG==1) putlog("creating substring");
	char *ret=(char*) calloc (length+1,sizeof(char)); //malloc(sizeof(char)*(length+1));
	ret[length]=0;int i;
	for (i=0;i<length;i++){
		ret[i]=text[i+first];
		//if (ret[i]==0) ret[i]='0';
	}
	if (spcKill==1){
	   for (i=length-1;i>=0;i--){
           if (ret[i]==32) ret[i]=0;
           else i=-1;
       }
    }
    //if (DEBUG==1) putlog("substring created");
	return ret;
}

static char *substring(char *text, int first, int length){return subString(text,first,length,0);} //1
*/

static char *tagExtract(char *tag, int tagLen, char* info){
//if (DEBUG==1) putlog("extracting tag");
	int pos, len, i;
	pos=inStr(tag,tagLen,info);
//hexchat_printf(ph,"pos=%i",pos);
	if (pos==-1) return "";//NULL;
	//printf("position of %s = %i\n",info,pos);
	len=0;
	//for (i=pos;i<pos+10;i++)printf("tag[%i]=%i \n",i,tag[i]);
	for (i=0;i<4;i++) {
		len+=tag[pos+strlen(info)+i]*iPow(255,3-i);
	}
	//printf("Tag-Length: %i\n",len);
	if (strcmp("COMM",info)!=0) return substring(tag,pos+7+strlen(info),len-1);//11
	return substring(tag,pos+7+strlen(info),len-1);//11
	//char *ct=substring(tag,pos+7+strlen(info),len-1);//11
	//return substring(ct,strlen(ct)+1,len-1-strlen(ct)); //<-- do not understand, what i did here :(
	
}

struct tagInfo readID3V1(char *file){
//if (DEBUG==1) putlog("reading ID3V1");
	FILE *f;
	struct tagInfo ret;
	int res, i, c, val;
	char *tag;
	char *id;
	char *tmp;
	tag = (char*) malloc(sizeof(char)*129);
	ret.artist=NULL;
	f=fopen(file,"rb");
	if (f==NULL){
       hexchat_print(ph,"file not found while trying to read id3v1");
       //if (DEBUG==1) putlog("file not found while trying to read id3v1");
       return ret;
    }
	//int offset=getSize(file)-128;
	res=fseek(f,-128,SEEK_END);
	if (res!=0) {printf("seek failed\n");fclose(f);return ret;}
	//long int pos=ftell(f);
	//printf("position= %li\n",pos);
	for (i=0;i<128;i++) {
		c=fgetc(f);
		if (c==EOF) {hexchat_printf(ph,"read ID3V1 failed\n");fclose(f);return ret;}
		tag[i]=(char)c;
	}
	fclose(f);
	//printf("tag readed: \n");
	id=substring(tag,0,3);
	//printf("header: %s\n",id);
	if (strcmp(id,"TAG")!=0){hexchat_printf(ph,"no id3 v1 found\n");return ret;}
	ret.title=subString(tag,3,30,1);
	ret.artist=subString(tag,33,30,1);
	ret.album=subString(tag,63,30,1);
	ret.comment=subString(tag,97,30,1);
	tmp=substring(tag,127,1);
	//ret.genre=substring(tag,127,1);
	
	val=(int)tmp[0];
	if (val<0)val+=256;
	//hexchat_printf(ph, "tmp[0]=%i (%i)",val,tmp[0]);
	if ((val<148)&&(val>=0)) 
       ret.genre=GENRES[val];//#############changed
	else {
         ret.genre="unknown";
         //hexchat_printf(ph, "tmp[0]=%i (%i)",val,tmp[0]);
    }
	//hexchat_printf(ph, "tmp: \"%s\" -> %i",tmp,tmp[0]);
	//hexchat_printf(ph,"genre \"%s\"",ret.genre);
	//if (DEBUG==1) putlog("id3v1 extracted");
	return ret;
}

char *extractID3Genre(char *tag){
     //if (DEBUG==1) putlog("extracting id3 genre");
     if (tag[strlen(tag)-1]==')'){
        tag[strlen(tag)-1]=0;
        tag=&tag[1];
        return GENRES[str2int(tag)];
        //return tag;
     }
     else{
          int i;
          //hexchat_print(ph, "Using 2 criteria");
          for (i=0;i<strlen(tag);i++){
              if (tag[i]==')'){ tag=&tag[i]+1;return tag;}
          //return tag;
          }
     }
     return "[152] failed";
}

struct tagInfo readID3V2(char *file){
//if (DEBUG==1) putlog("reading id3v2");
	FILE *f;
	int i, c, len;
	char header[10];
	char *tag;
	struct tagInfo ret;

	f = fopen(file,"rb");
	//hexchat_printf(ph,"file :%s",file);
	if (f==NULL)
	{
       hexchat_print(ph,"file not found whilt trying to read ID3V2");
       //if (DEBUG==1)putlog("file not found while trying to read ID3V2");
       return ret;
    }

	ret.artist=NULL;
	for (i=0;i<10;i++){
        c=fgetc(f);
        if (c==EOF){
           //putlog("found eof while reading id3v2");
           return ret;
        }
        header[i]=(char)c;
    }
	if (strstr(header,"ID3")==header){
		//hexchat_printf(ph,"found id3v2\n");
		len=0;
		for (i=6;i<10;i++) len+=(int)header[i]*iPow(256,9-i);
		
		//char *tag=(char*)malloc(sizeof(char)*len);
		tag=(char*) calloc(len,sizeof(char)); //malloc(sizeof(char)*len);
		for (i=0;i<len;i++){c=fgetc(f);tag[i]=(char)c;}
//hexchat_printf(ph,"tag length: %i\n",len);
//hexchat_printf(ph,"tag: %s\n",tag);
		fclose(f);
		ret.comment=tagExtract(tag,len,"COMM");
//hexchat_printf(ph,"Comment: %s\n",ret.comment);
		ret.genre=tagExtract(tag,len,"TCON");
		//if (strcmp(ret.genre,"(127)")==0) ret.genre="unknown";
//hexchat_printf(ph, "ret.genre = %s",ret.genre);
		if ((ret.genre!=NULL)&&(ret.genre[0]=='(')) ret.genre=extractID3Genre(ret.genre);
//hexchat_printf(ph,"genre: %s\n",ret.genre);
		ret.title=tagExtract(tag,len,"TIT2");
//hexchat_printf(ph,"Title: %s\n",ret.title);
		ret.album=tagExtract(tag,len,"TALB");
//hexchat_printf(ph,"Album: %s\n",ret.album);
		ret.artist=tagExtract(tag,len,"TPE1");
//hexchat_printf(ph,"Artist: %s\n",ret.artist);
	}
	else{fclose(f);printf("no id3v2 tag found\n"); return ret;}
	//printf("id2v2 done\n");
	//if (DEBUG==1) putlog("id3v2 readed");
	return ret;
}

struct tagInfo readHeader(char *file){
//if (DEBUG==1) putlog("reading header");
	FILE *f;
	//int buffer[5120];
	int versionB, layerB, bitrateB, freqB, modeB;
	int header[4];
	int count=0;
	int cc=0;
	struct tagInfo info;
	info.artist=NULL;

	f = fopen(file,"rb");
	if (f==NULL)
	{
       hexchat_print(ph,"file not found while trying to read mp3 header");
       //if (DEBUG==1) putlog("file not found while trying to read mp3 header");
       return info;
    }
	//struct tagInfo tagv2
	
	info=readID3V2(file);
	//struct tagInfo tagv1;//=readID3V1(file);
	//if 	(tagv2.artist!=NULL){info=tagv2;}
	//else {
	if (info.artist==NULL){
		//printf("searching for id3v1\n");
		//tagv1=readID3V1(file);
		info=readID3V1(file); //#####################
	}
	/*
	if (tagv1.artist!=NULL){
		//printf("Artist: %s\nTitle: %s\nAlbum: %s\nComment: %s\nGenre: %s\n",tagv1.artist,tagv1.title,tagv1.album,tagv1.comment,tagv1.genre);
		info=tagv1;
	}
	*/
	while ((count<5120)&&(cc!=EOF)&&(cc!=255)) {cc=fgetc(f);count++;}
	if ((cc==EOF)||(count==5119)) printf("no header found\n");
	else {
		//printf("located header at %i\n",count);
		header[0]=255;
		for (count=1;count<4;count++){
			header[count]=fgetc(f);
			//printf("header[%i]=%i\n",count,header[count]);
		}
		versionB=(header[1]&8)>>3;
		layerB=(header[1]&6)>>1;
		bitrateB=(header[2]&240)>>4; //4
		freqB=(header[2]&12)>>2;//2
		modeB=(header[3]&192)>>6;//6
		//printf("Mpeg: %i\nLayer: %i\nBitrate: %i\nFreq: %i\nMode: %i\n",versionB, layerB, bitrateB, freqB, modeB);
		//int Bitrate=RATES[versionB][layerB-1][bitrateB];
		//int Freq=FREQS[versionB][freqB];
		info.bitrate=RATES[versionB][layerB-1][bitrateB];
		info.freq=FREQS[versionB][freqB];
		info.mode=modeB;	
	}
	fclose(f);
	//if (DEBUG==1) putlog("header readed");
	return info;
}
/*
static void printMp3Info(char *file){
	//printf("\nScanning Mp3-File for Informations: %s\n",file);
	//printf("size:\t%10d byte\n",getSize(file));
	struct tagInfo info =readHeader(file);
	printf("%s | %10d",file,getSize(file));
	if  (info.bitrate>0){
		//printf("Bitrate: %i\nFreq: %i\nMode: %s\n",info.bitrate,info.freq,MODES[info.mode]);
		printf(" | %i kbps | %i kHz | %s",info.bitrate,info.freq,MODES[info.mode]);
		//if (info.artist!=NULL) printf("\nArtist: %s\nTitle: %s\nAlbum: %s\nComment: %s\nGenre: %s\n",info.artist,info.title,info.album,info.comment,info.genre);
		if (info.artist!=NULL) {
			printf("| %s | %s | %s | %s | %s",info.artist,info.title,info.album,info.comment,info.genre);
			//printf("| %s ",info.title);//,info.title,info.album,info.comment,info.genre
		}
	}
	printf("\n");
	
}
*/
ss="k">return; po++; } /* Allow only commands from the above list */ for (i = 0; i < ARRAY_SIZE (commands); i++) { char *cmd = commands[i]; int len = strlen (cmd); if (strncmp (cmd, po, len) == 0) { po += len; break; } } if (i == ARRAY_SIZE (commands)) return; /* Skip past the channel name or user nick */ po = strchr (po, ' '); if (!po) return; po++; g_regex_match(re_url(), po, 0, &gmi); while (g_match_info_matches(gmi)) { int start, end; g_match_info_fetch_pos(gmi, 0, &start, &end); while (end > start && (po[end - 1] == '\r' || po[end - 1] == '\n')) end--; url_add(po + start, end - start); g_match_info_next(gmi, NULL); } g_match_info_free(gmi); } int url_last (int *lstart, int *lend) { *lstart = laststart; *lend = lastend; return lasttype; } static gboolean regex_match (const GRegex *re, const char *word, int *start, int *end) { GMatchInfo *gmi; g_regex_match (re, word, 0, &gmi); if (!g_match_info_matches (gmi)) { g_match_info_free (gmi); return FALSE; } while (g_match_info_matches (gmi)) { g_match_info_fetch_pos (gmi, 0, start, end); g_match_info_next (gmi, NULL); } g_match_info_free (gmi); return TRUE; } /* Miscellaneous description --- */ #define DOMAIN "[a-z0-9][-a-z0-9]*(\\.[-a-z0-9]+)*" #define TLD "\\.[a-z][-a-z0-9]*[a-z]" #define IPADDR "[0-9]{1,3}(\\.[0-9]{1,3}){3}" #define IPV6GROUP "([0-9a-f]{0,4})" #define IPV6ADDR "((" IPV6GROUP "(:" IPV6GROUP "){7})" \ "|(" IPV6GROUP "(:" IPV6GROUP ")*:(:" IPV6GROUP ")+))" /* with :: compression */ #define HOST "(" DOMAIN TLD "|" IPADDR "|" IPV6ADDR ")" /* In urls the IPv6 must be enclosed in square brackets */ #define HOST_URL "(" DOMAIN TLD "|" IPADDR "|" "\\[" IPV6ADDR "\\]" ")" #define HOST_URL_OPT_TLD "(" DOMAIN "|" HOST_URL ")" #define PORT "(:[1-9][0-9]{0,4})" #define OPT_PORT "(" PORT ")?" static GRegex * make_re (char *grist) { GRegex *ret; GError *err = NULL; ret = g_regex_new (grist, G_REGEX_CASELESS | G_REGEX_OPTIMIZE, 0, &err); return ret; } /* HOST description --- */ /* (see miscellaneous above) */ static const GRegex * re_host (void) { static GRegex *host_ret; if (host_ret) return host_ret; host_ret = make_re ("(" "(" HOST_URL PORT ")|(" HOST ")" ")"); return host_ret; } static const GRegex * re_host6 (void) { static GRegex *host6_ret; if (host6_ret) return host6_ret; host6_ret = make_re ("(" "(" IPV6ADDR ")|(" "\\[" IPV6ADDR "\\]" PORT ")" ")"); return host6_ret; } /* URL description --- */ #define SCHEME "(%s)" #define LPAR "\\(" #define RPAR "\\)" #define NOPARENS "[^() \t]*" #define PATH \ "(" \ "(" LPAR NOPARENS RPAR ")" \ "|" \ "(" NOPARENS ")" \ ")*" /* Zero or more occurrences of either of these */ \ "(?<![.,?!\\]])" /* Not allowed to end with these */ #define USERINFO "([-a-z0-9._~%]+(:[-a-z0-9._~%]*)?@)" /* Flags used to describe URIs (RFC 3986) * * Bellow is an example of what the flags match. * * URI_AUTHORITY - http://example.org:80/foo/bar * ^^^^^^^^^^^^^^^^ * URI_USERINFO/URI_OPT_USERINFO - http://user@example.org:80/foo/bar * ^^^^^ * URI_PATH - http://example.org:80/foo/bar * ^^^^^^^^ */ #define URI_AUTHORITY (1 << 0) #define URI_OPT_USERINFO (1 << 1) #define URI_USERINFO (1 << 2) #define URI_PATH (1 << 3) struct { const char *scheme; /* scheme name. e.g. http */ const char *path_sep; /* string that begins the path */ int flags; /* see above (flag macros) */ } uri[] = { { "irc", "/", URI_PATH }, { "ircs", "/", URI_PATH }, { "rtsp", "/", URI_AUTHORITY | URI_PATH }, { "feed", "/", URI_AUTHORITY | URI_PATH }, { "teamspeak", "?", URI_AUTHORITY | URI_PATH }, { "ftp", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "sftp", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "ftps", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "http", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "https", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "cvs", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "svn", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "git", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "bzr", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "rsync", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "mumble", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "ventrilo", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "xmpp", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "h323", ";", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "imap", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "pop", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "nfs", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "smb", "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { "ssh", "", URI_AUTHORITY | URI_OPT_USERINFO }, { "sip", "", URI_AUTHORITY | URI_USERINFO }, { "sips", "", URI_AUTHORITY | URI_USERINFO }, { "magnet", "?", URI_PATH }, { "mailto", "", URI_PATH }, { "bitcoin", "", URI_PATH }, { "gtalk", "", URI_PATH }, { "steam", "", URI_PATH }, { "file", "/", URI_PATH }, { "callto", "", URI_PATH }, { "skype", "", URI_PATH }, { "geo", "", URI_PATH }, { "spotify", "", URI_PATH }, { "lastfm", "/", URI_PATH }, { "xfire", "", URI_PATH }, { NULL, "", 0} }; static const GRegex * re_url_no_scheme (void) { static GRegex *url_ret = NULL; if (url_ret) return url_ret; url_ret = make_re ("(" HOST_URL OPT_PORT "/" "(" PATH ")?" ")"); return url_ret; } static const GRegex * re_url (void) { static GRegex *url_ret = NULL; GString *grist_gstr; char *grist; int i; if (url_ret) return url_ret; grist_gstr = g_string_new (NULL); for (i = 0; uri[i].scheme; i++) { if (i) g_string_append (grist_gstr, "|"); g_string_append (grist_gstr, "("); g_string_append_printf (grist_gstr, "%s:", uri[i].scheme); if (uri[i].flags & URI_AUTHORITY) g_string_append (grist_gstr, "//"); if (uri[i].flags & URI_USERINFO) g_string_append (grist_gstr, USERINFO); else if (uri[i].flags & URI_OPT_USERINFO) g_string_append (grist_gstr, USERINFO "?"); if (uri[i].flags & URI_AUTHORITY) g_string_append (grist_gstr, HOST_URL_OPT_TLD OPT_PORT); if (uri[i].flags & URI_PATH) { char *sep_escaped; sep_escaped = g_regex_escape_string (uri[i].path_sep, strlen(uri[i].path_sep)); g_string_append_printf(grist_gstr, "(" "%s" PATH ")?", sep_escaped); g_free(sep_escaped); } g_string_append(grist_gstr, ")"); } grist = g_string_free (grist_gstr, FALSE); url_ret = make_re (grist); g_free (grist); return url_ret; } /* EMAIL description --- */ #define EMAIL "[a-z][-_a-z0-9]+@" "(" HOST_URL ")" static const GRegex * re_email (void) { static GRegex *email_ret; if (email_ret) return email_ret; email_ret = make_re ("(" EMAIL ")"); return email_ret; } /* NICK description --- */ /* For NICKPRE see before url_check_word() */ #define NICKHYP "-" #define NICKLET "a-z" #define NICKDIG "0-9" /* Note for NICKSPE: \\\\ boils down to a single \ */ #define NICKSPE "\\[\\]\\\\`_^{|}" #if 0 #define NICK0 "[" NICKPRE "]?[" NICKLET NICKSPE "]" #else /* Allow violation of rfc 2812 by allowing digit as first char */ /* Rationale is that do_an_re() above will anyway look up what */ /* we find, and that WORD_NICK is the last item in the array */ /* that do_an_re() runs through. */ #define NICK0 "^[" NICKPRE "]?[" NICKLET NICKDIG NICKSPE "]" #endif #define NICK1 "[" NICKHYP NICKLET NICKDIG NICKSPE "]*" #define NICK NICK0 NICK1 static const GRegex * re_nick (void) { static GRegex *nick_ret; if (nick_ret) return nick_ret; nick_ret = make_re ("(" NICK ")"); return nick_ret; } /* CHANNEL description --- */ #define CHANNEL "[" CHANPRE "][^ \t\a,]+(?:,[" CHANPRE "][^ \t\a,]+)*" static const GRegex * re_channel (void) { static GRegex *channel_ret; if (channel_ret) return channel_ret; channel_ret = make_re ("(" CHANNEL ")"); return channel_ret; } /* PATH description --- */ #ifdef WIN32 /* Windows path can be .\ ..\ or C: D: etc */ #define FS_PATH "^(\\.{1,2}\\\\|[a-z]:).*" #else /* Linux path can be / or ./ or ../ etc */ #define FS_PATH "^(/|\\./|\\.\\./).*" #endif static const GRegex * re_path (void) { static GRegex *path_ret; if (path_ret) return path_ret; path_ret = make_re ("(" FS_PATH ")"); return path_ret; }