 /******************************************************************************
 * program:     wp2latex                                                      *
 * function:    module with XML support functions for HTML, AbiWord ans Accent*
 * modul:       pass1xml.cc                                                   *
 * description: 							      *
 * licency:     GPL		                                              *
 ******************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#include<stringa.h>
#include<lists.h>
#include<dbllist.h>

#include"wp2latex.h"
#include"pass1xml.h"
#include "cp_lib/cptran.h"


// See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types
const TMimeItem MimeList[] =
{
  {"data:image/bmp;base64", 21, ".bmp"},
  {"data:image/gif;base64", 21, ".gif"},
  {"data:image/jpg;base64", 21, ".jpg"},
  {"data:image/jpeg;base64",22, ".jpg"},
  {"data:image/png;base64", 21, ".png"},
  {"data:image/svg+xml;base64", 25, ".svg"},
  {"data:image/tiff;base64",22, ".tif"},
  {"data:image/tif;base64", 21, ".tif"}
};


// static const char *base64_chars = 
//             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
//             "abcdefghijklmnopqrstuvwxyz"
//             "0123456789+/";
uint8_t FindBase64(char ch)
{
  if(ch>='A' && ch<='Z') return ch - 'A';
  if(ch>='a' && ch<='z') return ch - 'a' + 'Z'-'A'+1;
  if(ch>='0' && ch<='9') return ch - '0' + 2*('Z'-'A')+2;
  if(ch=='+') return 2*('Z'-'A')+2 + 10;  
  if(ch=='/') return 2*('Z'-'A')+2 + 11;
return 0xFF;
}


void base64_decode(const char *encoded_string, FILE *FOut)
{
int in_len = strlen(encoded_string);
int i = 0;
int in_ = 0;
uint8_t char_array_4[4], char_array_3[3];

  while(in_len--)
    {
    const char ch = encoded_string[in_++];
    if(ch=='\r'|| ch=='\n')
        continue;
    if(ch=='=' || !is_base64(ch))
        break;
    char_array_4[i++] = FindBase64(ch);

    if(i==4) 
      {
      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

      fwrite(char_array_3,1,3,FOut);
      i = 0;
      }
    }

  if(i)
  {
    memset(char_array_4+i, FindBase64(0), 4-i);

    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

    fwrite(&char_array_3, 1, i-1, FOut);
  }

  return;
}


/////////////////////////////////////////////////////////////////


void TconvertedPass1_XML::SelectTranslator(const char *CharSet)
{
string translator;
  while(*CharSet==' ') CharSet++;

  if(!strcmp(CharSet,"utf8") || !strcmp(CharSet,"utf-8") || 
     !strcmp(CharSet,"UTF8") || !strcmp(CharSet,"UTF-8"))
    {
    ConvertCpg = GetTranslator("unicodeTOinternal");
    CharReader = &utf8_fgetc;
    goto Finish;
    }
  
  if(!strncmp(CharSet,"iso-8859-",9) || !strncmp(CharSet,"ISO-8859-",9))
  {
    translator = "iso_8859_";
    translator += CharSet+9;
  }
  else if(!stricmp(CharSet,"windows-1250")) translator="cp1250";
  else if(!stricmp(CharSet,"windows-1251")) translator="cp1251";
  else if(!stricmp(CharSet,"windows-1252")) translator="cp1252";
  else translator=CharSet;

  translator += "TOinternal";
  ConvertCpg = GetTranslator(translator);
  CharReader = &ch_fgetc;

Finish:
  if(ConvertCpg==NULL || ConvertCpg->number()==0)
    if(err != NULL)
      {
      perc.Hide();
      fprintf(err,_("\nError: Cannot initialize charset converter %s!"),translator());
      }
}


/// Read one XML object from wpd stream. See ::XML_STATUS.
/// cq->by = 0 (normal char), 1 (extended char &xxx;), 2 (tag <>), 3 (end tag </>), 4 comment, 5 unfinished &xxx, 
///          6 expanded unicode, 7 CDATA block, 127 fail
void TconvertedPass1_XML::ReadXMLTag(bool MakeUpper)
{
#ifdef DEBUG
  fprintf(log,"\n#ReadXMLTag() ");fflush(log);
#endif
int c;
string Attribute, Value;

erase(TAG_Args);
c = CharReader(wpd);
if((unsigned)c==0xFFFF)
  {by=XML_fail;erase(TAG);return;}

if(c>=0x100)	//Handle unicode character
	{
	TAG=Ext_chr_str(c,this,ConvertCpg);
	by = XML_unicode;
	return;
	}

if(c=='<')	//Handle HTML tag
{
  by = XML_tag;
  TAG = '<';
  c=fgetc(wpd);
  if(c=='/') by=XML_closetag;

  while(!feof(wpd))
  {
    if(c=='>') {TAG += MakeUpper?toupper(c):c; return;}
    if(isspace(c))
    {
      if(TAG[1]=='!' && TAG[2]=='-' && TAG[3]=='-')
      {
        unsigned char Status = 0;        

        Value.erase();
        do {
          if((c=fgetc(wpd)) == EOF) return;    
          switch(c)
          {     
            case 0: return;			 //illegal 0 char
            case '-':if(Status<2) Status++;
                    Value += c;
                    break;
            case '>':if(Status==2)
                    {
                      Status++;
                      break;
                    }
		    // fall down no break.
            default:Status = 0;
                    Value += c;		//read whole comment
                    break;
          }          
        } while(Status<3);
        by = XML_comment;
        return;
      }
      TAG += '>';

/*if(TAG=="<image>")
  c='i';*/

      while(!feof(wpd))	// read arg list
      {
        Attribute.erase();
        Value.erase();
        do {
	   c=fgetc(wpd);
	   } while (c==' ' || c=='\r' || c=='\n');
        if(c=='>') break;
        while(c!='=' && c!='>')
        {
          Attribute += c;
          if( (c=fgetc(wpd)) == EOF) return;
          if(c==0) return;			 //illegal 0 char
        }
	if(c!='>')		//Attribute without value <x aaa>
	   {
	   do {
	      c = fgetc(wpd);
	      } while (isspace(c));
	   if(c=='"')		//read "value" in double quotation mark
	     {
	     c=fgetc(wpd);
	     while(c!='\"')
	       {
	       Value+=c;
	       if( (c=fgetc(wpd)) == EOF) break;
	       if(c==0) {c='>';break;}			//illegal 0 char
	       }
	     }
	  else			//read "value" without double quotation mark
	     {
	     while(c!='>' && !isspace(c))
		{
		Value+=c;
		if( (c=fgetc(wpd)) == EOF) break;
		if(c==0) {c='>';break;}			//illegal 0 char
		}
	     }
	  }
        if(Value.isEmpty() && c=='>' && Attribute=='/')
        {
          if(by==XML_closedtag) by=XML_closedclosetag;
          if(by==XML_tag) by=XML_closedtag;
          break;
        }
	TAG_Args.Add(Attribute(),Value());
	if(c=='>') break;
	}
        return;
    }

    TAG += MakeUpper ? toupper(c) : c;

    if(c=='[' && !strncmp(TAG(),"<![CDATA[",9))
          {
          char c2;
          do {
             c2 = c;
             if( (c=fgetc(wpd)) == EOF)
               {by=XML_fail; return;}
             if(c==0) continue;			 //illegal 0 char
	     TAG += c;		//read all CDATA
	     } while (c2!=']' || c!='>');
          by = XML_CDATA;
          return;
          }

	c = fgetc(wpd);
	}

   TAG += '>';			//EOF reached
   return;
   }

if(c=='&')
   {
   by = XML_extchar;

   TAG = c;
   while(!feof(wpd))
       {
       c=fgetc(wpd);
       if(c==';') break;
       if(isspace(c) || c==0)	//unterminated & sequence
	   {			//space also terminates &xx; section (CR, LF, TAB and ' ')
	   by = XML_badextchar;
	   break;
	   }

       TAG+=c;
       }
   TAG+=';';
   return;
   }

by = XML_char;
subby = c;
TAG = c;
}


/** This function converts comment inside HTML. */
void TconvertedPass1_XML::CommentXML(void)
{
#ifdef DEBUG
  fprintf(log,"\n#CommentXML() ");fflush(log);
#endif
  static const char ObjName[] = "!Comment";
  signed char Old_char_on_line;
  unsigned char OldFlag;
  attribute OldAttr;
  int i,maxi,commentchars;

  if(strip==NULL)
  {
    strcpy(ObjType, ObjName);
    return;
  }

  OldFlag = flag;
  OldAttr = attr;
  Old_char_on_line = char_on_line;
  flag = CharsOnly;
  recursion++;
  attr.InitAttr();		//Turn all attributes in the comment off

  fputc('%',strip);

  maxi = length(TAG);
  i = 0;
  commentchars = 0;

  if(!strncmp(TAG(),"<![CDATA[",9))
    {
    i += 9;
    if(TAG[maxi-1]=='>')
      {
      maxi--;
      if(TAG[maxi-1]==']')
        {
        maxi--;
        if(TAG[maxi-1]==']') maxi--;
        }
      }
    }
  else
    {
    if(TAG[i]=='<' && TAG[i+1]=='!' && TAG[maxi-1]=='>') {i+=2;maxi--;}
    if(TAG[i]=='-' && TAG[maxi-1]=='-' && i<maxi) {i++;maxi--;}
    if(TAG[i]=='-' && TAG[maxi-1]=='-' && i<maxi) {i++;maxi--;}
    }
  while(TAG[i]==' ' && TAG[maxi-1]==' ' && i<maxi) {i++;maxi--;}

  while (i<maxi)
       {
       by=TAG[i++];
       if(by==0) break;

       if(by==0xA || by==0xD)	//New comment line
		{
		if(commentchars)
		  {
		  line_term = 's';    	//Soft return
		  Make_tableentry_envir_extra_end(this);
		  fprintf(strip, "\n");
		  rownum++;
		  Make_tableentry_attr(this);

		  fputc('%',strip);
		  commentchars=0;
		  }
		continue;
		}

       fputc(by,strip);
       commentchars++;
       continue;
       }

  line_term = 's';    	//Soft return
  Make_tableentry_envir_extra_end(this);
  fprintf(strip, "\n");
  rownum++;
  Make_tableentry_attr(this);


  recursion--;
  strcpy(ObjType, ObjName+1);
  attr = OldAttr;
  flag = OldFlag;
  if(Old_char_on_line==CHAR_PRESENT || Old_char_on_line==JUNK_CHARS) char_on_line = JUNK_CHARS;
	else char_on_line = NO_CHAR;
}
