increase number of scraper buffers to 20 (from trunk). thanks cptspiff.
[xbmc:xbmc-antiquated.git] / XBMC / xbmc / utils / ScraperParser.cpp
1 #include "ScraperParser.h"
2
3 #ifdef _LINUX
4 #include "system.h"
5 #endif
6
7 #include "stdafx.h"
8
9 #include <cstring>
10 #include "RegExp.h"
11 #include "HTMLUtil.h"
12 #include "CharsetConverter.h"
13 #include "URL.h"
14 #include "HTTP.h"
15 #include "FileSystem/FileZip.h"
16
17 #include "Picture.h"
18 #include "Util.h"
19
20 #include <sstream>
21
22 using namespace std;
23
24 CScraperUrl::CScraperUrl(const CStdString& strUrl)
25 {
26   ParseString(strUrl);
27 }
28
29 CScraperUrl::CScraperUrl(const TiXmlElement* element)
30 {
31   ParseElement(element);
32 }
33
34 CScraperUrl::CScraperUrl()
35 {
36 }
37
38 CScraperUrl::~CScraperUrl()
39 {
40 }
41
42 void CScraperUrl::Clear()
43 {
44   m_url.clear();
45   m_spoof.clear();
46   m_xml.clear();
47 }
48
49 bool CScraperUrl::Parse()
50 {
51   return ParseString(m_xml);
52 }
53
54 bool CScraperUrl::ParseElement(const TiXmlElement* element)
55 {
56         if (!element || !element->FirstChild()) return false;
57
58         std::stringstream stream;
59         stream << *element;
60         m_xml += stream.str();
61         bool bHasChilds = false;
62         if (element->FirstChildElement("thumb")) 
63         {
64                 element = element->FirstChildElement("thumb");
65                 bHasChilds = true;
66         }
67         else if (element->FirstChildElement("url")) 
68         {
69                 element = element->FirstChildElement("url");
70                 bHasChilds = true;
71         }
72         while (element)
73         {
74                 SUrlEntry url;
75                 url.m_url = element->FirstChild()->Value();
76                 const char* pSpoof = element->Attribute("spoof");
77                 if (pSpoof)
78                         url.m_spoof = pSpoof;
79                 const char* szPost=element->Attribute("post");
80                 if (szPost && stricmp(szPost,"yes") == 0)
81                         url.m_post = true;
82                 else
83                         url.m_post = false;
84     const char* pCache = element->Attribute("cache");
85     if (pCache)
86       url.m_cache = pCache;
87
88                 const char* szType = element->Attribute("type");
89                 url.m_type = URL_TYPE_GENERAL;
90                 if (szType && stricmp(szType,"season") == 0)
91                 {
92                         url.m_type = URL_TYPE_SEASON;
93                         const char* szSeason = element->Attribute("season");
94                         if (szSeason)
95                                 url.m_season = atoi(szSeason);
96                         else
97                                 url.m_season = -1;
98                 }
99                 else
100                         url.m_season = -1;
101
102                 m_url.push_back(url);
103                 if (bHasChilds)
104                 {
105                         const TiXmlElement* temp = element->NextSiblingElement("thumb");
106                         if (temp)
107                                 element = temp;
108                         else
109                                 element = element->NextSiblingElement("url");
110                 }
111                 else
112                         element = NULL;
113         }
114         return true;
115 }
116
117 bool CScraperUrl::ParseString(CStdString strUrl)
118 {
119   if (strUrl.IsEmpty())
120     return false;
121   
122   // ok, now parse the xml file
123   if (strUrl.Find("encoding=\"utf-8\"") < 0)
124     g_charsetConverter.stringCharsetToUtf8(strUrl);
125   
126   TiXmlDocument doc;
127   doc.Parse(strUrl.c_str(),0,TIXML_ENCODING_UTF8);
128   m_xml += strUrl;
129
130   TiXmlElement* pElement = doc.RootElement();
131   if (pElement)
132     ParseElement(pElement);
133   else
134   {
135     SUrlEntry url;
136     url.m_url = strUrl;
137     url.m_type = URL_TYPE_GENERAL;
138     url.m_season = -1;
139     url.m_post = false;
140     m_url.push_back(url);
141   }
142   return true;
143 }
144
145 const CScraperUrl::SUrlEntry CScraperUrl::GetFirstThumb() const
146 {
147   for (std::vector<SUrlEntry>::const_iterator iter=m_url.begin();iter != m_url.end();++iter)
148   {
149     if (iter->m_type == URL_TYPE_GENERAL)
150       return *iter;
151   }
152   SUrlEntry result;
153   result.m_season = -1;
154   return result;
155 }
156
157 const CScraperUrl::SUrlEntry CScraperUrl::GetSeasonThumb(int season) const
158 {
159   for (std::vector<SUrlEntry>::const_iterator iter=m_url.begin();iter != m_url.end();++iter)
160   {
161     if (iter->m_type == URL_TYPE_SEASON && iter->m_season == season)
162       return *iter;
163   }
164   SUrlEntry result;
165   result.m_season = -1;
166   return result;
167 }
168
169 bool CScraperUrl::Get(const SUrlEntry& scrURL, string& strHTML, CHTTP& http)
170 {
171   CURL url(scrURL.m_url);
172   http.SetReferer(scrURL.m_spoof);
173   CStdString strCachePath;
174
175   if (!scrURL.m_cache.IsEmpty())
176   {
177     CUtil::AddFileToFolder(g_advancedSettings.m_cachePath,"scrapers\\"+scrURL.m_cache,strCachePath);
178     if (XFILE::CFile::Exists(strCachePath))
179     {
180       XFILE::CFile file;
181       file.Open(strCachePath);
182       char* temp = new char[(int)file.GetLength()];
183       file.Read(temp,file.GetLength());
184       strHTML.append(temp,temp+file.GetLength());
185       file.Close();
186       delete[] temp;
187       return true;
188     }
189   }
190
191   if (scrURL.m_post)
192   {
193     CStdString strOptions = url.GetOptions();
194     strOptions = strOptions.substr(1);
195     url.SetOptions("");
196     CStdString strUrl;
197     url.GetURL(strUrl);
198
199     if (!http.Post(strUrl, strOptions, strHTML))
200       return false;
201   }
202   else 
203     if (!http.Get(scrURL.m_url, strHTML))
204       return false;
205
206   if (scrURL.m_url.Find(".zip") > -1)
207   {
208     XFILE::CFileZip file;
209     CStdString strBuffer;
210     int iSize = file.UnpackFromMemory(strBuffer,strHTML);
211     if (iSize)
212     {
213       strHTML.clear();
214       strHTML.append(strBuffer.c_str(),strBuffer.data()+iSize);      
215     }
216   }
217
218   if (!scrURL.m_cache.IsEmpty())
219   {
220     CStdString strCachePath;
221     CUtil::AddFileToFolder(g_advancedSettings.m_cachePath,"scrapers\\"+scrURL.m_cache,strCachePath);
222     XFILE::CFile file;
223     if (file.OpenForWrite(strCachePath,true,true))
224       file.Write(strHTML.data(),strHTML.size());
225     file.Close();
226   }
227   return true;
228 }
229
230 bool CScraperUrl::DownloadThumbnail(const CStdString &thumb, const CScraperUrl::SUrlEntry& entry)
231 {
232   if (entry.m_url.IsEmpty())
233     return false;
234
235   CHTTP http;
236   http.SetReferer(entry.m_spoof);
237   string thumbData;
238   if (http.Get(entry.m_url, thumbData))
239   {
240     try
241     {
242       CPicture picture;
243       picture.CreateThumbnailFromMemory((const BYTE *)thumbData.c_str(), thumbData.size(), CUtil::GetExtension(entry.m_url), thumb);
244       return true;
245     }
246     catch (...)
247     {
248       ::DeleteFile(thumb.c_str());
249     }
250   }
251   return false;
252 }
253
254 bool CScraperUrl::ParseEpisodeGuide(CStdString strUrls)
255 {
256   if (strUrls.IsEmpty())
257     return false;
258
259   // ok, now parse the xml file
260   if (strUrls.Find("encoding=\"utf-8\"") < 0)
261     g_charsetConverter.stringCharsetToUtf8(strUrls);
262
263   TiXmlDocument doc;
264   doc.Parse(strUrls.c_str(),0,TIXML_ENCODING_UTF8);
265   if (doc.RootElement())
266   {
267     TiXmlHandle docHandle( &doc );
268     TiXmlElement *link = docHandle.FirstChild( "episodeguide" ).FirstChild( "url" ).Element();
269     while (link)
270     {
271       ParseElement(link);
272       link = link->NextSiblingElement("url");
273     } 
274   }
275   else
276     return false;
277   return true;
278 }
279
280 CScraperParser::CScraperParser()
281 {
282   m_pRootElement = NULL;
283   m_name = m_content = NULL;
284   m_document = NULL;
285   m_settings = NULL;
286 }
287
288 CScraperParser::~CScraperParser()
289 {
290   m_pRootElement = NULL;
291   if (m_document)
292     delete m_document;
293
294   m_document = NULL;
295   m_name = m_content = NULL;
296   m_settings = NULL;
297 }
298
299 bool CScraperParser::Load(const CStdString& strXMLFile)
300 {
301   if (m_document)
302     return true;
303
304   m_document = new TiXmlDocument(_P(strXMLFile).c_str());
305
306   if (!m_document)
307     return false;
308
309   if (m_document->LoadFile())
310   {
311     m_pRootElement = m_document->RootElement();
312     CStdString strValue = m_pRootElement->Value();
313     if (strValue != "scraper")
314     {
315       delete m_document;
316       m_document = NULL;
317       m_pRootElement = NULL;
318       return false;
319     }
320
321     m_name = m_pRootElement->Attribute("name");
322     m_content = m_pRootElement->Attribute("content");
323
324     if (!m_name || !m_content) // FIXME
325     {
326       delete m_document;
327       m_document = NULL;
328       m_pRootElement = NULL;
329       return false;
330     }
331     // check for known content
332     if (stricmp(m_content,"tvshows") && stricmp(m_content,"movies") && stricmp(m_content,"musicvideos") && stricmp(m_content,"albums"))
333     {
334       delete m_document;
335       m_document = NULL;
336       m_pRootElement = NULL;
337       return false;
338     }
339   }
340   else
341   {
342     delete m_document;
343     m_document = NULL;
344     return false;
345   }
346
347   return true;
348 }
349
350 void CScraperParser::ReplaceBuffers(CStdString& strDest)
351 {
352   char temp[5];
353   // insert buffers
354   int iIndex;
355   for (int i=0;i<MAX_SCRAPER_BUFFERS;++i)
356   {
357     iIndex = 0;
358     sprintf(temp,"$$%i",i+1);
359     while ((size_t)(iIndex = strDest.find(temp,iIndex)) != CStdString::npos) // COPIED FROM CStdString WITH THE ADDITION OF $ ESCAPING
360     {
361       strDest.replace(strDest.begin()+iIndex,strDest.begin()+iIndex+strlen(temp),m_param[i]);
362       iIndex += m_param[i].length();
363     }
364   }
365   // insert settings
366   iIndex = 0;
367   while ((size_t)(iIndex = strDest.find("$INFO[",iIndex)) != CStdString::npos && m_settings)
368   {
369     int iEnd = strDest.Find("]",iIndex);
370     CStdString strInfo = strDest.Mid(iIndex+6,iEnd-iIndex-6);
371     CStdString strReplace = m_settings->Get(strInfo);
372     strDest.replace(strDest.begin()+iIndex,strDest.begin()+iEnd+1,strReplace);
373     iIndex += strReplace.length();
374   }
375   iIndex = 0;
376   while ((size_t)(iIndex = strDest.find("\\n",iIndex)) != CStdString::npos)
377     strDest.replace(strDest.begin()+iIndex,strDest.begin()+iIndex+2,"\n");
378 }
379
380 void CScraperParser::ParseExpression(const CStdString& input, CStdString& dest, TiXmlElement* element, bool bAppend)
381 {
382   CStdString strOutput = element->Attribute("output");
383
384   TiXmlElement* pExpression = element->FirstChildElement("expression");
385   if (pExpression)
386   {
387     CRegExp reg;
388     CStdString strExpression;
389     if (pExpression->FirstChild())
390       strExpression = pExpression->FirstChild()->Value();
391     else
392       strExpression = "(.*)";
393     ReplaceBuffers(strExpression);
394     ReplaceBuffers(strOutput);
395
396     if (!reg.RegComp(strExpression.c_str()))
397     {
398       //std::cout << "error compiling regexp in scraper";
399       return; 
400     }
401
402     bool bRepeat = false;
403     const char* szRepeat = pExpression->Attribute("repeat");
404     if (szRepeat)
405       if (stricmp(szRepeat,"yes") == 0)
406         bRepeat = true;
407
408     const char* szClear = pExpression->Attribute("clear");
409     if (szClear)
410       if (stricmp(szClear,"yes") == 0)
411         dest=""; // clear no matter if regexp fails
412
413     const char* szNoClean = pExpression->Attribute("noclean");
414     bool bClean[MAX_SCRAPER_BUFFERS];
415     for (int iBuf=0;iBuf<MAX_SCRAPER_BUFFERS;++iBuf)
416       bClean[iBuf] = true;
417     if (szNoClean)
418     {
419       std::vector<CStdString> vecBufs;
420       CUtil::Tokenize(szNoClean,vecBufs,",");
421       for (size_t nToken=0; nToken < vecBufs.size(); nToken++)
422         bClean[atoi(vecBufs[nToken].c_str())-1] = false;
423     }
424
425     const char* szTrim = pExpression->Attribute("trim");
426     bool bTrim[MAX_SCRAPER_BUFFERS];
427     for (int iBuf=0;iBuf<MAX_SCRAPER_BUFFERS;++iBuf)
428       bTrim[iBuf] = false;
429     if (szTrim)
430     {
431       std::vector<CStdString> vecBufs;
432       CUtil::Tokenize(szTrim,vecBufs,",");
433       for (size_t nToken=0; nToken < vecBufs.size(); nToken++)
434         bTrim[atoi(vecBufs[nToken].c_str())-1] = true;
435     }
436
437     int iOptional = -1;
438     pExpression->QueryIntAttribute("optional",&iOptional);
439
440     int iCompare = -1;
441     pExpression->QueryIntAttribute("compare",&iCompare);
442     if (iCompare > -1)
443       m_param[iCompare-1].ToLower();
444     CStdString curInput = input;
445     for (int iBuf=0;iBuf<MAX_SCRAPER_BUFFERS;++iBuf)
446     {
447       if (bClean[iBuf])
448       {
449         char temp[4];
450         sprintf(temp,"\\%i",iBuf+1);
451         size_t i2=0;
452         while ((i2 = strOutput.Find(temp,i2)) != CStdString::npos)
453         {
454           strOutput.Insert(i2,"!!!CLEAN!!!");
455           i2 += 11;
456           strOutput.Insert(i2+2,"!!!CLEAN!!!");
457           i2 += 2;
458         }
459       }
460       if (bTrim[iBuf])
461       {
462         char temp[4];
463         sprintf(temp,"\\%i",iBuf+1);
464         size_t i2=0;
465         while ((i2 = strOutput.Find(temp,i2)) != CStdString::npos)
466         {
467           strOutput.Insert(i2,"!!!TRIM!!!");
468           i2 += 10;
469           strOutput.Insert(i2+2,"!!!TRIM!!!");
470           i2 += 2;
471         }
472       }
473     }
474     int i = reg.RegFind(curInput.c_str());
475     while (i > -1 && (i < (int)curInput.size() || curInput.size() == 0))
476     {
477       if (!bAppend)
478       {
479         dest = "";
480         bAppend = true;
481       }
482       CStdString strCurOutput=strOutput;
483
484       if (iOptional > -1) // check that required param is there
485       {
486         char temp[4];
487         sprintf(temp,"\\%i",iOptional);
488         char* szParam = reg.GetReplaceString(temp);
489         CRegExp reg2;
490         reg2.RegComp("(.*)(\\\\\\(.*\\\\2.*)\\\\\\)(.*)");
491         int i2=reg2.RegFind(strCurOutput.c_str());
492         while (i2 > -1)
493         {
494           char* szRemove = reg2.GetReplaceString("\\2");
495           int iRemove = strlen(szRemove);
496           int i3 = strCurOutput.find(szRemove);
497           if (szParam && strcmp(szParam,""))
498           {
499             strCurOutput.erase(i3+iRemove,2);
500             strCurOutput.erase(i3,2);
501           }
502           else
503             strCurOutput.replace(strCurOutput.begin()+i3,strCurOutput.begin()+i3+iRemove+2,"");
504
505           free(szRemove);
506
507           i2 = reg2.RegFind(strCurOutput.c_str());
508         }
509         if (szParam)
510           free(szParam);
511       }
512
513       int iLen = reg.GetFindLen();
514       // nasty hack #1 - & means \0 in a replace string
515       strCurOutput.Replace("&","!!!AMPAMP!!!");
516       char* result = reg.GetReplaceString(strCurOutput.c_str());
517       if (result && strlen(result))
518       {
519         CStdString strResult(result);
520         strResult.Replace("!!!AMPAMP!!!","&");
521         Clean(strResult);
522         ReplaceBuffers(strResult);
523         if (iCompare > -1)
524         {
525           CStdString strResultNoCase = strResult;
526           strResultNoCase.ToLower();
527           if ((size_t) strResultNoCase.Find(m_param[iCompare-1]) != CStdString::npos)
528             dest += strResult;
529         }
530         else
531           dest += strResult;
532
533         free(result);
534       }
535       if (bRepeat)
536       {
537         curInput.erase(0,i+iLen>(int)curInput.size()?curInput.size():i+iLen);
538         i = reg.RegFind(curInput.c_str());
539       }
540       else
541         i = -1;
542     }
543   }
544 }
545
546 void CScraperParser::ParseNext(TiXmlElement* element)
547 {
548   TiXmlElement* pReg = element;
549   while (pReg)
550   {
551     TiXmlElement* pChildReg = pReg->FirstChildElement("RegExp");
552     if (pChildReg)
553       ParseNext(pChildReg);
554     else
555     {
556       TiXmlElement* pChildReg = pReg->FirstChildElement("clear");
557       if (pChildReg)
558         ParseNext(pChildReg);
559     }   
560
561     int iDest = 1;
562     bool bAppend = false;
563     const char* szDest = pReg->Attribute("dest");
564     if (szDest)
565       if (strlen(szDest))
566       {
567         if (szDest[strlen(szDest)-1] == '+')
568           bAppend = true;
569
570         iDest = atoi(szDest);
571       }
572
573       const char *szInput = pReg->Attribute("input");
574       CStdString strInput;
575       if (szInput)
576       {
577         strInput = szInput;
578         ReplaceBuffers(strInput);
579       }
580       else
581         strInput = m_param[0];
582
583       const char* szConditional = pReg->Attribute("conditional");
584       bool bExecute = true;
585       if (szConditional)
586       {
587         bool bInverse=false;
588         if (szConditional[0] == '!')
589         {
590           bInverse = true;
591           szConditional++;
592         }
593         CStdString strSetting;
594         if (m_settings)
595            strSetting = m_settings->Get(szConditional);
596         if (strSetting.IsEmpty()) // setting isnt around - treat as if the value is false
597           bExecute = !bInverse;
598         else
599           bExecute = bInverse?!strSetting.Equals("true"):strSetting.Equals("true");
600       }
601
602       if (bExecute)
603         ParseExpression(strInput, m_param[iDest-1],pReg,bAppend);
604
605       pReg = pReg->NextSiblingElement("RegExp");
606   }
607 }
608
609 const CStdString CScraperParser::Parse(const CStdString& strTag, CScraperSettings* pSettings)
610 {
611   TiXmlElement* pChildElement = m_pRootElement->FirstChildElement(strTag.c_str());
612   if(pChildElement == NULL) return "";
613   int iResult = 1; // default to param 1
614   pChildElement->QueryIntAttribute("dest",&iResult);
615   TiXmlElement* pChildStart = pChildElement->FirstChildElement("RegExp");
616   if (pSettings)
617     m_settings = pSettings;
618   else
619     m_settings = NULL;
620   ParseNext(pChildStart);
621   CStdString tmp = m_param[iResult-1];
622   
623   const char* szClearBuffers = pChildElement->Attribute("clearbuffers");
624   if (!szClearBuffers || stricmp(szClearBuffers,"no") != 0)
625     ClearBuffers(); 
626
627   return tmp;
628 }
629
630 bool CScraperParser::HasFunction(const CStdString& strTag)
631 {
632   TiXmlElement* pChildElement = m_pRootElement->FirstChildElement(strTag.c_str());
633   
634   if (!pChildElement) 
635     return false;
636   
637   return true;
638 }
639
640 void CScraperParser::Clean(CStdString& strDirty)
641 {
642   size_t i=0;
643   CStdString strBuffer;
644   while ((i=strDirty.Find("!!!CLEAN!!!",i)) != CStdString::npos)
645   {
646     size_t i2;
647     if ((i2=strDirty.Find("!!!CLEAN!!!",i+11)) != CStdString::npos)
648     {
649       strBuffer = strDirty.substr(i+11,i2-i-11);
650       //char* szConverted = ConvertHTMLToAnsi(strBuffer.c_str());
651       //const char* szTrimmed = RemoveWhiteSpace(szConverted);
652       CStdString strConverted(strBuffer);
653       HTML::CHTMLUtil::RemoveTags(strConverted);
654       const char* szTrimmed = RemoveWhiteSpace(strConverted.c_str());
655       strDirty.erase(i,i2-i+11);
656       strDirty.Insert(i,szTrimmed);
657       i += strlen(szTrimmed);
658       //free(szConverted);
659     }
660     else
661       break;
662   }
663   i=0;
664   while ((i=strDirty.Find("!!!TRIM!!!",i)) != CStdString::npos)
665   {
666     size_t i2;
667     if ((i2=strDirty.Find("!!!TRIM!!!",i+10)) != CStdString::npos)
668     {
669       strBuffer = strDirty.substr(i+10,i2-i-10);
670       const char* szTrimmed = RemoveWhiteSpace(strBuffer.c_str());
671       strDirty.erase(i,i2-i+10);
672       strDirty.Insert(i,szTrimmed);
673       i += strlen(szTrimmed);
674     }
675     else
676       break;
677   }
678 }
679
680 char* CScraperParser::ConvertHTMLToAnsi(const char *szHTML)
681 {
682   if (!szHTML)
683     return NULL;
684
685   int i=0; 
686   int len = (int)strlen(szHTML);
687   if (len == 0)
688     return NULL;
689
690   int iAnsiPos=0;
691   char *szAnsi = (char *)malloc(len*2*sizeof(char));
692
693   while (i < len)
694   {
695     char kar=szHTML[i];
696     if (kar=='&')
697     {
698       if (szHTML[i+1]=='#')
699       {
700         int ipos=0;
701         char szDigit[12];
702         i+=2;
703         if (szHTML[i+2]=='x') i++;
704
705         while ( ipos < 12 && szHTML[i] && isdigit(szHTML[i])) 
706         {
707           szDigit[ipos]=szHTML[i];
708           szDigit[ipos+1]=0;
709           ipos++;
710           i++;
711         }
712
713         // is it a hex or a decimal string?
714         if (szHTML[i+2]=='x')
715           szAnsi[iAnsiPos++] = (char)(strtol(szDigit, NULL, 16) & 0xFF);
716         else
717           szAnsi[iAnsiPos++] = (char)(strtol(szDigit, NULL, 10) & 0xFF);
718         i++;
719       }
720       else
721       {
722         i++;
723         int ipos=0;
724         char szKey[112];
725         while (szHTML[i] && szHTML[i] != ';' && ipos < 12)
726         {
727           szKey[ipos]=(unsigned char)szHTML[i];
728           szKey[ipos+1]=0;
729           ipos++;
730           i++;
731         }
732         i++;
733         if (strcmp(szKey,"amp")==0)          szAnsi[iAnsiPos++] = '&';
734         else if (strcmp(szKey,"quot")==0)    szAnsi[iAnsiPos++] = (char)0x22;
735         else if (strcmp(szKey,"frasl")==0)   szAnsi[iAnsiPos++] = (char)0x2F;
736         else if (strcmp(szKey,"lt")==0)      szAnsi[iAnsiPos++] = (char)0x3C;
737         else if (strcmp(szKey,"gt")==0)      szAnsi[iAnsiPos++] = (char)0x3E;
738         else if (strcmp(szKey,"trade")==0)   szAnsi[iAnsiPos++] = (char)0x99;
739         else if (strcmp(szKey,"nbsp")==0)    szAnsi[iAnsiPos++] = ' ';
740         else if (strcmp(szKey,"iexcl")==0)   szAnsi[iAnsiPos++] = (char)0xA1;
741         else if (strcmp(szKey,"cent")==0)    szAnsi[iAnsiPos++] = (char)0xA2;
742         else if (strcmp(szKey,"pound")==0)   szAnsi[iAnsiPos++] = (char)0xA3;
743         else if (strcmp(szKey,"curren")==0)  szAnsi[iAnsiPos++] = (char)0xA4;
744         else if (strcmp(szKey,"yen")==0)     szAnsi[iAnsiPos++] = (char)0xA5;
745         else if (strcmp(szKey,"brvbar")==0)  szAnsi[iAnsiPos++] = (char)0xA6;
746         else if (strcmp(szKey,"sect")==0)    szAnsi[iAnsiPos++] = (char)0xA7;
747         else if (strcmp(szKey,"uml")==0)     szAnsi[iAnsiPos++] = (char)0xA8;
748         else if (strcmp(szKey,"copy")==0)    szAnsi[iAnsiPos++] = (char)0xA9;
749         else if (strcmp(szKey,"ordf")==0)    szAnsi[iAnsiPos++] = (char)0xAA;
750         else if (strcmp(szKey,"laquo")==0)   szAnsi[iAnsiPos++] = (char)0xAB;
751         else if (strcmp(szKey,"not")==0)     szAnsi[iAnsiPos++] = (char)0xAC;
752         else if (strcmp(szKey,"shy")==0)     szAnsi[iAnsiPos++] = (char)0xAD;
753         else if (strcmp(szKey,"reg")==0)     szAnsi[iAnsiPos++] = (char)0xAE;
754         else if (strcmp(szKey,"macr")==0)    szAnsi[iAnsiPos++] = (char)0xAF;
755         else if (strcmp(szKey,"deg")==0)     szAnsi[iAnsiPos++] = (char)0xB0;
756         else if (strcmp(szKey,"plusmn")==0)  szAnsi[iAnsiPos++] = (char)0xB1;
757         else if (strcmp(szKey,"sup2")==0)    szAnsi[iAnsiPos++] = (char)0xB2;
758         else if (strcmp(szKey,"sup3")==0)    szAnsi[iAnsiPos++] = (char)0xB3;
759         else if (strcmp(szKey,"acute")==0)   szAnsi[iAnsiPos++] = (char)0xB4;
760         else if (strcmp(szKey,"micro")==0)   szAnsi[iAnsiPos++] = (char)0xB5;
761         else if (strcmp(szKey,"para")==0)    szAnsi[iAnsiPos++] = (char)0xB6;
762         else if (strcmp(szKey,"middot")==0)  szAnsi[iAnsiPos++] = (char)0xB7;
763         else if (strcmp(szKey,"cedil")==0)   szAnsi[iAnsiPos++] = (char)0xB8;
764         else if (strcmp(szKey,"sup1")==0)    szAnsi[iAnsiPos++] = (char)0xB9;
765         else if (strcmp(szKey,"ordm")==0)    szAnsi[iAnsiPos++] = (char)0xBA;
766         else if (strcmp(szKey,"raquo")==0)   szAnsi[iAnsiPos++] = (char)0xBB;
767         else if (strcmp(szKey,"frac14")==0)  szAnsi[iAnsiPos++] = (char)0xBC;
768         else if (strcmp(szKey,"frac12")==0)  szAnsi[iAnsiPos++] = (char)0xBD;
769         else if (strcmp(szKey,"frac34")==0)  szAnsi[iAnsiPos++] = (char)0xBE;
770         else if (strcmp(szKey,"iquest")==0)  szAnsi[iAnsiPos++] = (char)0xBF;
771         else if (strcmp(szKey,"Agrave")==0)  szAnsi[iAnsiPos++] = (char)0xC0;
772         else if (strcmp(szKey,"Aacute")==0)  szAnsi[iAnsiPos++] = (char)0xC1;
773         else if (strcmp(szKey,"Acirc")==0)   szAnsi[iAnsiPos++] = (char)0xC2;
774         else if (strcmp(szKey,"Atilde")==0)  szAnsi[iAnsiPos++] = (char)0xC3;
775         else if (strcmp(szKey,"Auml")==0)    szAnsi[iAnsiPos++] = (char)0xC4;
776         else if (strcmp(szKey,"Aring")==0)   szAnsi[iAnsiPos++] = (char)0xC5;
777         else if (strcmp(szKey,"AElig")==0)   szAnsi[iAnsiPos++] = (char)0xC6;
778         else if (strcmp(szKey,"Ccedil")==0)  szAnsi[iAnsiPos++] = (char)0xC7;
779         else if (strcmp(szKey,"Egrave")==0)  szAnsi[iAnsiPos++] = (char)0xC8;
780         else if (strcmp(szKey,"Eacute")==0)  szAnsi[iAnsiPos++] = (char)0xC9;
781         else if (strcmp(szKey,"Ecirc")==0)   szAnsi[iAnsiPos++] = (char)0xCA;
782         else if (strcmp(szKey,"Euml")==0)    szAnsi[iAnsiPos++] = (char)0xCB;
783         else if (strcmp(szKey,"Igrave")==0)  szAnsi[iAnsiPos++] = (char)0xCC;
784         else if (strcmp(szKey,"Iacute")==0)  szAnsi[iAnsiPos++] = (char)0xCD;
785         else if (strcmp(szKey,"Icirc")==0)   szAnsi[iAnsiPos++] = (char)0xCE;
786         else if (strcmp(szKey,"Iuml")==0)    szAnsi[iAnsiPos++] = (char)0xCF;
787         else if (strcmp(szKey,"ETH")==0)     szAnsi[iAnsiPos++] = (char)0xD0;
788         else if (strcmp(szKey,"Ntilde")==0)  szAnsi[iAnsiPos++] = (char)0xD1;
789         else if (strcmp(szKey,"Ograve")==0)  szAnsi[iAnsiPos++] = (char)0xD2;
790         else if (strcmp(szKey,"Oacute")==0)  szAnsi[iAnsiPos++] = (char)0xD3;
791         else if (strcmp(szKey,"Ocirc")==0)   szAnsi[iAnsiPos++] = (char)0xD4;
792         else if (strcmp(szKey,"Otilde")==0)  szAnsi[iAnsiPos++] = (char)0xD5;
793         else if (strcmp(szKey,"Ouml")==0)    szAnsi[iAnsiPos++] = (char)0xD6;
794         else if (strcmp(szKey,"times")==0)   szAnsi[iAnsiPos++] = (char)0xD7;
795         else if (strcmp(szKey,"Oslash")==0)  szAnsi[iAnsiPos++] = (char)0xD8;
796         else if (strcmp(szKey,"Ugrave")==0)  szAnsi[iAnsiPos++] = (char)0xD9;
797         else if (strcmp(szKey,"Uacute")==0)  szAnsi[iAnsiPos++] = (char)0xDA;
798         else if (strcmp(szKey,"Ucirc")==0)   szAnsi[iAnsiPos++] = (char)0xDB;
799         else if (strcmp(szKey,"Uuml")==0)    szAnsi[iAnsiPos++] = (char)0xDC;
800         else if (strcmp(szKey,"Yacute")==0)  szAnsi[iAnsiPos++] = (char)0xDD;
801         else if (strcmp(szKey,"THORN")==0)   szAnsi[iAnsiPos++] = (char)0xDE;
802         else if (strcmp(szKey,"szlig")==0)   szAnsi[iAnsiPos++] = (char)0xDF;
803         else if (strcmp(szKey,"agrave")==0)  szAnsi[iAnsiPos++] = (char)0xE0;
804         else if (strcmp(szKey,"aacute")==0)  szAnsi[iAnsiPos++] = (char)0xE1;
805         else if (strcmp(szKey,"acirc")==0)   szAnsi[iAnsiPos++] = (char)0xE2;
806         else if (strcmp(szKey,"atilde")==0)  szAnsi[iAnsiPos++] = (char)0xE3;
807         else if (strcmp(szKey,"auml")==0)    szAnsi[iAnsiPos++] = (char)0xE4;
808         else if (strcmp(szKey,"aring")==0)   szAnsi[iAnsiPos++] = (char)0xE5;
809         else if (strcmp(szKey,"aelig")==0)   szAnsi[iAnsiPos++] = (char)0xE6;
810         else if (strcmp(szKey,"ccedil")==0)  szAnsi[iAnsiPos++] = (char)0xE7;
811         else if (strcmp(szKey,"egrave")==0)  szAnsi[iAnsiPos++] = (char)0xE8;
812         else if (strcmp(szKey,"eacute")==0)  szAnsi[iAnsiPos++] = (char)0xE9;
813         else if (strcmp(szKey,"ecirc")==0)   szAnsi[iAnsiPos++] = (char)0xEA;
814         else if (strcmp(szKey,"euml")==0)    szAnsi[iAnsiPos++] = (char)0xEB;
815         else if (strcmp(szKey,"igrave")==0)  szAnsi[iAnsiPos++] = (char)0xEC;
816         else if (strcmp(szKey,"iacute")==0)  szAnsi[iAnsiPos++] = (char)0xED;
817         else if (strcmp(szKey,"icirc")==0)   szAnsi[iAnsiPos++] = (char)0xEE;
818         else if (strcmp(szKey,"iuml")==0)    szAnsi[iAnsiPos++] = (char)0xEF;
819         else if (strcmp(szKey,"eth")==0)     szAnsi[iAnsiPos++] = (char)0xF0;
820         else if (strcmp(szKey,"ntilde")==0)  szAnsi[iAnsiPos++] = (char)0xF1;
821         else if (strcmp(szKey,"ograve")==0)  szAnsi[iAnsiPos++] = (char)0xF2;
822         else if (strcmp(szKey,"oacute")==0)  szAnsi[iAnsiPos++] = (char)0xF3;
823         else if (strcmp(szKey,"ocirc")==0)   szAnsi[iAnsiPos++] = (char)0xF4;
824         else if (strcmp(szKey,"otilde")==0)  szAnsi[iAnsiPos++] = (char)0xF5;
825         else if (strcmp(szKey,"ouml")==0)    szAnsi[iAnsiPos++] = (char)0xF6;
826         else if (strcmp(szKey,"divide")==0)  szAnsi[iAnsiPos++] = (char)0xF7;
827         else if (strcmp(szKey,"oslash")==0)  szAnsi[iAnsiPos++] = (char)0xF8;
828         else if (strcmp(szKey,"ugrave")==0)  szAnsi[iAnsiPos++] = (char)0xF9;
829         else if (strcmp(szKey,"uacute")==0)  szAnsi[iAnsiPos++] = (char)0xFA;
830         else if (strcmp(szKey,"ucirc")==0)   szAnsi[iAnsiPos++] = (char)0xFB;
831         else if (strcmp(szKey,"uuml")==0)    szAnsi[iAnsiPos++] = (char)0xFC;
832         else if (strcmp(szKey,"yacute")==0)  szAnsi[iAnsiPos++] = (char)0xFD;
833         else if (strcmp(szKey,"thorn")==0)   szAnsi[iAnsiPos++] = (char)0xFE;
834         else if (strcmp(szKey,"yuml")==0)    szAnsi[iAnsiPos++] = (char)0xFF;
835         else
836         {
837           // its not an ampersand code, so just copy the contents
838           szAnsi[iAnsiPos++] = '&';
839           for (unsigned int iLen=0; iLen<strlen(szKey); iLen++)
840             szAnsi[iAnsiPos++] = (unsigned char)szKey[iLen];
841         }
842       }
843     }
844     else
845     {
846       szAnsi[iAnsiPos++] = kar;
847       i++;
848     }
849   }
850   szAnsi[iAnsiPos++]=0;
851   return szAnsi;
852 }
853
854 char* CScraperParser::RemoveWhiteSpace(const char *string2)
855 {
856   if (!string2) return (char*)"";
857   char* string = (char*)string2;
858   size_t pos = strlen(string)-1;
859   while ((string[pos] == ' ' || string[pos] == '\n') && string[pos] && pos)
860     string[pos--] = '\0';
861   while ((*string == ' ' || *string == '\n') && *string != '\0')
862     string++;
863   return string;
864 }
865
866 void CScraperParser::ClearBuffers()
867 {
868   //clear all m_param strings
869   for (int i=0;i<MAX_SCRAPER_BUFFERS;++i)
870     m_param[i].clear();
871 }
872
873