
/* TODO: always add a new font to array if not set... ease removal */

/* this parser is getting silly... Aileron is an email client, not a 
 * freakin' web browser 
 */

/*  HTMLParser.c: the HTML parser that gives those nice text formats
 *
 *  This file is part of Aileron, a WINGs email client. 
 *
 *  Copyright (c) 1999 - 2001  Nwanua Elumeze
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "Aileron.h"

struct { 
  WMText *tPtr;
  WMArray *colors;
  WMArray *fonts;
  WMColor *ccolor;
  WMFont  *cfont;
  
  WMAlignment align:2;
  unsigned short u:1;
  unsigned short first:1;
  unsigned short prepend:1;
  unsigned short plainText:1;
  unsigned short tokenClosed:1;
  unsigned short escaping:1;

  unsigned short indent;
} Parser;


#define isSpace(c) (c == ' ')

#define isIgnore(s) (\
     (strCaseCmp(s, "3D")) \
  )



static
unsigned char map_table[256] = {
0, 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,
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, 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};

#define TOLOWER(x)  (map_table[(int)x]) 




int
strCaseCmp(const unsigned char *s1, const unsigned char *s2)
{
  if (!*s1 || !*s2) return 0;
  while (*s2 != '\0') {
    if (TOLOWER (*s1) != TOLOWER (*s2)) /* true if *s1 == 0 ! */
      return 0;
    s1++;
    s2++;
  }
  return (*s1=='\0' ||!isalnum(*s1))?1:0;
}


int
strNCaseCmp(const unsigned char *s1, const unsigned char *s2, int len)
{
  const unsigned char *s2ptr = s2;

  if (!*s1 || !*s2) return 0;
  while (*s2 != '\0' && (s2 - s2ptr) <= len) {
    if (TOLOWER (*s1) != TOLOWER (*s2)) /* true if *s1 == 0 ! */
      return 0;
    s1++;
    s2++;
  }
  return (1);
}

static void insertText (char *text, unsigned short len) {

  WMRulerMargins margins = {NULL, 0, 1000, 
    Parser.indent*20, Parser.indent*20, 0}; 
  void *tb;

 
  if(text && *text == '\n' && len>0) {
    Parser.first = 1;
    text++;
    len--;
  }

  tb = WMCreateTextBlockWithText(Parser.tPtr, text, 
    Parser.cfont, Parser.ccolor, 
    Parser.first, len);

  WMSetTextBlockProperties(Parser.tPtr, tb, Parser.first,
    0, Parser.u, 0, &margins);
   
  if (Parser.prepend)
    WMPrependTextBlock(Parser.tPtr, tb);
  else
    WMAppendTextBlock(Parser.tPtr, tb);

  Parser.first = 0;
}


static void doItalic(void)
{
   WMFont *prev = WMRetainFont(Parser.cfont);

   WMReleaseFont(Parser.cfont);

   Parser.cfont = WMRetainFont(
     Parser.tokenClosed ? WMUnemphasizeFont(Aileron.screen, prev)
                        : WMEmphasizeFont(Aileron.screen, prev));
       
   WMReleaseFont(prev);
}


static void doBold(void)
{
   WMFont *prev = WMRetainFont(Parser.cfont);

   WMReleaseFont(Parser.cfont);

   Parser.cfont = WMRetainFont(
     Parser.tokenClosed ? WMUnstrengthenFont(Aileron.screen, prev)
                        : WMStrengthenFont(Aileron.screen, prev));
       
   WMReleaseFont(prev);
}

/* doColor is really a helper function for <FONT COLOR="#0000FF"> */
void doColor(char *name)
{
  WMColor *color;

  if(!name)
    return;

  color = WMCreateNamedColor(Aileron.screen, name, False);

  if(color) { 
    WMPushInArray(Parser.colors, (void *)color);
    WMReleaseColor(Parser.ccolor);
    Parser.ccolor =  WMRetainColor(color); 
  }
}
  
char *
getURL(char *url)
{
  FILE *pipe;
  char *command;
  int bufSize = 512, i, itemsRead = 0;
  char buf[bufSize+1], *data = NULL;
  char *mark = NULL, path[256];
  
  if(!url)
    return NULL;
    
  if( (mark = strrchr(url, '/')) && *(mark+1)) { 
    snprintf(path, 255, "%s/%s", Aileron.tmpdir, mark+1);
    if(FileExists(path))
       return wstrdup(path);
  }

  command = wstrdup("wget -q -O  ");
  command = wstrappend(command, url);
  if(!(pipe = popen(command, "r"))) {
    wfree(command);
    return wstrdup(url);
  } 
  
  wfree(command);
  
  do {
    i = 0;
    i = fread(buf, 1, bufSize, pipe);
    if(!(i>0))
       break;
    data = wrealloc(data, sizeof(void *)*(itemsRead+i));
    memcpy(data + itemsRead, buf, i);
    itemsRead += i;
  } while(i != 0);

  pclose(pipe);
  if(data && itemsRead > 0) {
    FILE *file;
    
    if( (mark = strrchr(url, '/')) && *(mark+1)) { 
      snprintf(path, 255, "%s/%s", Aileron.tmpdir, mark+1);
    } else {
      char *tmp = wstrdup(Aileron.tmpdir);
      tmp = wstrappend(tmp, "/AItmp");
      tmp = wstrappend(tmp, ".XXXXXX");
      mkstemp(tmp); 
      snprintf(path, 255, tmp);
      wfree(tmp);
    }

    if((file = fopen(path, "w"))) { 
      fwrite(data, itemsRead, 1, file);
      fclose(file);
      wfree(data);
      return wstrdup(path);
    } else { 
      wfree(data);
      return wstrdup(url);
    } 
  } 
  
  
  return wstrdup(url);
} 


void doImage(WMArray *args)
{
  void *tb;
  WMPixmap *p;
  WMRulerMargins margins = {NULL, 0, 1000, 
    Parser.indent*20, Parser.indent*20, 0}; 
  int i, c = WMGetArrayItemCount(args);
  char *url = NULL, *arg = NULL;

  for(i=1; i<c; i++) {
    arg = (char *)WMGetFromArray(args, i);
    if(!arg)
      break;
  
    if(i<c-1) { 
      if(strCaseCmp(arg, "src")) { 
        i++;
        arg = (char *)WMGetFromArray(args, i);
        if(!arg)
          break;

        if(strlen(arg)>7 && (strNCaseCmp(arg, "http://", 7) 
          || strNCaseCmp(arg, "ftp://", 6))) { 
          printf("getting %s\n", arg);
          url = getURL(arg);
        } else url = wstrdup(arg); 

        if(!url)
          break;
        p = WMCreatePixmapFromFile(Aileron.screen, url);
        if(!p) 
          p = WMRetainPixmap(Aileron.ImageIcon);
 
        tb = WMCreateTextBlockWithPixmap(Parser.tPtr, p, 
          url, Parser.ccolor, Parser.first, TYPEIMAGE); 
 
        WMSetTextBlockProperties(Parser.tPtr, tb, Parser.first,
          0, Parser.u, 0, &margins);
   
        if (Parser.prepend)
           WMPrependTextBlock(Parser.tPtr, tb);
         else
           WMAppendTextBlock(Parser.tPtr, tb);

         Parser.first = 0;
        
         wfree(url);
         WMReleasePixmap(p);
      }
    }
  } 
}


void doHREF(WMArray *args)
{
  int c = WMGetArrayItemCount(args);
  int i;
  char *arg = NULL;


  for(i=1; i<c; i++) {
    arg = (char *)WMGetFromArray(args, i);
    if(!arg)
      break;

    if(i<c-1) { 
      if(strCaseCmp(arg, "src")) { 
        i++;
        arg = (char *)WMGetFromArray(args, i);
        if(!arg)
          break;
        doImage(args);
      }
    }

  }
}

void doFont(WMArray *args)
{
  int c = WMGetArrayItemCount(args);
  int i;
  char *arg = NULL;

  for(i=1; i<c; i++) {
    arg = (char *)WMGetFromArray(args, i);
    if(!arg)
      break;

    if(i<c-1) { 
      if(strCaseCmp(arg, "color")) { 
        i++;
        arg = (char *)WMGetFromArray(args, i);
        if(!arg)
          break;
        doColor(arg);
      }
    }

  }
}

void doInput(WMArray *args)
{
#if 0
  int c = WMGetArrayItemCount(args);
  int i;
  char *arg = NULL;
  
  
  for(i=1; i<c; i++) {
    arg = (char *)WMGetFromArray(args, i);
    if(!arg)
      break;
      
    if(i<c-1) {
      if(strCaseCmp(arg, "type")) {
        i++;
        arg = (char *)WMGetFromArray(args, i);
        if(!arg)
          break;
        if(strCaseCmp(arg, "checkbox")) {
          WMRulerMargins margins = {NULL, 0, 1000, 
            Parser.indent*20, Parser.indent*20, 0}; 
          WMButton *b = WMCreateSwitchButton(Parser.tPtr);
          void *tb = WMCreateTextBlockWithObject(Parser.tPtr, b,
             "checkbox", Parser.ccolor, Parser.first, TYPEAPPLICATION);
          
          WMSetTextBlockProperties(Parser.tPtr, tb, Parser.first,
            0, Parser.u, 0, &margins);
          
          if (Parser.prepend)
             WMPrependTextBlock(Parser.tPtr, tb);
          else
             WMAppendTextBlock(Parser.tPtr, tb);
        }
      } 
    }
  }
#endif
} 



void doBody(WMArray *args)
{
  int c = WMGetArrayItemCount(args);
  int i;
  char *arg = NULL;
  WMColor *color;
  

  for(i=1; i<c; i++) {
    arg = (char *)WMGetFromArray(args, i);
    if(!arg)
      break;

    if(i<c-1) { 
      if(strCaseCmp(arg, "bgcolor")) { 
        i++;
        arg = (char *)WMGetFromArray(args, i);
        if(!arg)
          break;
        color = WMCreateNamedColor(Aileron.screen, arg, True);
        if(color) { 
          WMSetTextBackgroundColor(Parser.tPtr, color);
          WMReleaseColor(color);
        }
      }
    }

  }
}

/* <button src=foo text=hoo w=10 h=10 action=aaaa> */
void doButton(WMArray *args)
{
  void *tb;
  WMButton *b;
  int i, c = WMGetArrayItemCount(args);
  char *arg = NULL;
  WMRulerMargins margins = {NULL, 0, 1000, 
    Parser.indent*20, Parser.indent*20, 0}; 

  for(i=1; i<c; i++) {
    arg = (char *)WMGetFromArray(args, i);
    if(!arg)
      break;
  
    if(i<c-1) { 
/*
      if(strCaseCmp(arg, "w")) { 
        i++;
        arg = (char *)WMGetFromArray(args, i);
        if(!arg)
          break;
*/

      if(strCaseCmp(arg, "src")) { 
        i++;
        arg = (char *)WMGetFromArray(args, i);
        if(!arg)
          break;

/*      b = CreateBlendedButton(Parser.tPtr, WBTMomentaryPush,
      "Aileron", arg); */

        b = WMCreateCommandButton(Parser.tPtr);
        WMSetButtonText(b, "Open Attachment");
        WMResizeWidget(b, 100, 18);
        WMSetButtonFont(b, Aileron.buttonfont);



        tb = WMCreateTextBlockWithObject(Parser.tPtr, b, 
          arg, Parser.ccolor, Parser.first, TYPEAPPLICATION); 
 
        WMSetTextBlockProperties(Parser.tPtr, tb, Parser.first,
          0, Parser.u, 0, &margins);
   
        if (Parser.prepend)
           WMPrependTextBlock(Parser.tPtr, tb);
         else
           WMAppendTextBlock(Parser.tPtr, tb);

         Parser.first = 0;
        
      }
    }
  } 
}

static void 
releaseArrayItems(void *data)
{
  if(data)
    wfree(data);
}  


static void 
releaseFonts(void *data)
{
  if(data)
    WMReleaseFont((WMFont *)data);
}  

static void 
releaseColors(void *data)
{
  if(data)
    WMReleaseColor((WMColor *)data);
}  

static char *mystrchrs(char *haystack)
{
  char *ptr = haystack;

  if(!haystack)
    return NULL;

  while(*ptr) { 
    if(*ptr == ' ' || *ptr == '=' || *ptr == '\"')
        return ptr;
    ptr++;
  }

  return NULL;

}


static void
ridTokenofBS(char **token)
{
  char *start, *mark;

  start = *token;
  while(start) { 
    if((mark = strstr(start, "=3D"))) { 
      *(mark+1) = *(mark+2) = ' ';
      start = mark + 3;
    } else start = NULL;
  }

  start = *token;
  while(start) { 
    if((mark = strstr(start, "=2E"))) { 
      *mark = '.';
      memmove(mark+1, mark+3, strlen(start)-(mark-start));
      start = mark + 3;
     } else start = NULL;
  }
}


static void doWithArgs(char *token)
{
  WMArray *args;
  char *start, *mark;
  short foundQuote=0;

  if(!(start = token)) 
    return;

  ridTokenofBS(&token);

  args = WMCreateArrayWithDestructor(4, releaseArrayItems);

  while(start) {

    if(!foundQuote) { 
      mark = mystrchrs(start);
      if(mark && *mark == '\"')
          foundQuote = 1;

    } else {
      mark = strchr(start, '\"');
      if(mark)
        foundQuote = !foundQuote;
    }

    if(mark) { 
      unsigned long len = ((int)(mark-start));
      char c[len+1];

      memcpy(c, start, len);
      c[len] = 0;
      if(len && !isIgnore(c))
        WMAddToArray(args, (void *)wstrdup(c));
      start = mark + 1;

    } else { 
      unsigned long len = strlen(start);
      char c[len+1];

      memcpy(c, start, len);
      c[len] = 0;
      if(len) 
        WMAddToArray(args, (void *)wstrdup(c));
      start = mark; 
     }
  }

 
  if(WMGetArrayItemCount(args) > 0) { 
    char *token = (char *)WMGetFromArray(args, 0);

    if(strCaseCmp(token, "img")) {
      doImage(args);
 
    } else if(strCaseCmp(token, "button")) {
      doButton(args);
 
    } else if(strCaseCmp(token, "font")) {
      doFont(args);

    } else if(strCaseCmp(token, "a")) { 
      doHREF(args);

    } else if(!strncmp(token, "DEFANGED", 8)) {
       ;
    } else if(strCaseCmp(token, "BODY")) {
      doBody(args);
    } else if(strCaseCmp(token, "input")) {
      doInput(args);
    } else if(strCaseCmp(token, "div")) {
         ;
    } else if(strCaseCmp(token, "td")) {
         ;
    } else if(strCaseCmp(token, "tr")) {
         ;
    } else if(strCaseCmp(token, "table")) {
         ;
    } else if(strCaseCmp(token, "map")) {
         ;
    } else if(strCaseCmp(token, "area")) {
         ;
    } else if(strCaseCmp(token, "meta")) {
         ;
    } else if(strCaseCmp(token, "form")) {
         ;
    } else {
      printf("DEBUG: unknown token with args [%s]\n", token);
      insertText (token, strlen(token));
    }
   
  }

  WMFreeArray(args);
}



static void parseToken(char *token)
{
  int len;

  if(!token)
    return;

  while(*token && isSpace(*(token))) 
    token++;

  if(*token == '/') {
    token++;
    Parser.tokenClosed = 1;
    while(*token && isSpace(*(token))) 
      token++;
  }
  
  if(!(*token))
    return;
   
  if(*token == '!')
    return;

  len = strlen(token);

  if(len == 1) { 

_parseSingleToken:
     switch(TOLOWER(*token)) { 
       case 'i': 
         doItalic();
         break;

       case 'b':  
         doBold();
         break;

       case 'p': {
         Parser.first = 1;
         insertText(NULL, 0);
       } break;

       case 'u':
        Parser.u = !Parser.tokenClosed;
        break;
      }


    } else if(len == 2) { 

     if(isSpace(*(token+1))) { 
       goto _parseSingleToken;
     
     } if(TOLOWER(*token) == 'b' && TOLOWER(*(token+1)) == 'r') {
       Parser.first = 1;
     
     } else if(TOLOWER(*token) == 'u' && TOLOWER(*(token+1)) == 'l') {
       if(Parser.tokenClosed && Parser.indent>0)  
         Parser.indent--;
       if(!Parser.tokenClosed)  
         Parser.indent++;
         
     } else if(TOLOWER(*token) == 'l' && TOLOWER(*(token+1)) == 'i') {
       Parser.first = 1;
       /* also, create a new margin.... */
 
     } 

    } else { 
      char *ptr = token;

      ptr++;
      while(*ptr) {
        if(!isSpace(*ptr))
          goto _parseContinue;
        ptr++;
      }
      goto _parseSingleToken;

    
_parseContinue:
      if(strchr(token, '=')) { 
        doWithArgs(token); 

      } else if(strCaseCmp(token, "center")) {
        Parser.align = (Parser.tokenClosed? WALeft : WACenter);

      } else if(strCaseCmp(token, "font")) {
        int count = WMGetArrayItemCount(Parser.colors);

        if(Parser.tokenClosed && count>0) {
          WMPopFromArray(Parser.colors);
          //WMReleaseColor(Parser.ccolor);

          if(--count > 0) { 
	        Parser.ccolor =  
              WMRetainColor(WMGetFromArray(Parser.colors, count-1));
          } else {
            Parser.ccolor =  WMRetainColor(WMGetTextDefaultColor(Parser.tPtr));
          }
        }

      } else if(!strncmp(token, "!--", 3)) {
        ;
      } else if(strCaseCmp(token, "html")) {
        Parser.plainText = Parser.tokenClosed;

      } else if(strCaseCmp(token, "hline")) {
      printf("doHline\n");

      } else if(strCaseCmp(token, "div")) {
        ;
      } else if(strCaseCmp(token, "style")) {
        ;
      } else if(strCaseCmp(token, "table")) {
        ;
      } else if(strCaseCmp(token, "title")) {
        ; /* what would that mean for mail? */
      } else if(strCaseCmp(token, "head")) {
        ; 
      } else if(strCaseCmp(token, "body")) {
        ;
      } else if(strCaseCmp(token, "map")) {
        ;
      } else if(strCaseCmp(token, "form")) {
        ;
    } else if(!strncmp(token, "DEFANGED", 8)) {
        ;
    } else if(strCaseCmp(token, "BODY")) {
      } else if(strchr(token, '@')) {  
        char *text = wstrdup("<");
        void *tb;

        text = wstrappend(text, token);
        text = wstrappend(text, ">");

        tb = WMCreateTextBlockWithText(Parser.tPtr, text, 
          Parser.cfont, Parser.ccolor, 
          Parser.first, strlen(text));
 
        WMAppendTextBlock(Parser.tPtr, tb);
        wfree(text);

      } else {  
        printf("DEBUG: unknown token [%s]\n", token);
      }
   }

}



#define BLOCK_TEXT_INCR 8
#define reqBlockSize(requested) (requested+BLOCK_TEXT_INCR)


static void
parse (char *stream) {
  char *token = NULL, *text = NULL, c, p=0;
  unsigned short tokenlen=0, textlen=0, wasspace=0;
  unsigned short tokenAlloc=0, textAlloc=0;


  while( (c=*(stream++))) {
  
    if(p == '=' && c == ' ') {
      p = c;
      if(tokenlen>0) 
        --tokenlen;
      continue;
    }

    if(c == '\n' || c =='\t') {
      if(c == '\n' && Parser.plainText && Parser.tokenClosed) { 
         if(textlen>0) {
           insertText (text, textlen);
           textlen = 0;
         }
         insertText(NULL, 0);
         Parser.first = 1;

      } else {
        p = c;
        continue;
      }
    }

    if(0&&!Parser.plainText) { 
      if(isSpace(c)) {
        if(wasspace) { 
          p = c;
          continue;
        }
        wasspace = 1;
  
      }else wasspace = 0;
    }

    if(c == '&' && !Parser.escaping) {
      Parser.escaping = 1;
      if(textlen>0) {
        insertText (text, textlen);
        textlen = 0;
      }
      while(*stream &&  (c=*(stream++)) != ';');
      if(!stream) { 
        if(text) {
          wfree(text);
          text = NULL;
        }

        if(token) { 
          tokenlen = 0;
          wfree(token);
          token = NULL;
        }
         
        return;
      }
      Parser.escaping = 0;

     } else  if(c == '<'  && Parser.tokenClosed) {
      Parser.tokenClosed = 0;
      if(textlen>0) {
        insertText (text, textlen);
        textlen = 0;
      }

    } else if(c == '>' && !Parser.tokenClosed) {
      if(tokenlen>0) {
        token[tokenlen] = 0;
        parseToken(token);
        tokenlen = 0;
      }
      Parser.tokenClosed = 1;

     } else {
      if(!Parser.tokenClosed) {
        if(tokenlen+BLOCK_TEXT_INCR >= tokenAlloc) { 
          tokenAlloc = reqBlockSize(tokenlen);
          token = wrealloc(token, tokenAlloc);
        }
        token[tokenlen++] = c;

       } else { 
         if(textlen+BLOCK_TEXT_INCR >= textAlloc) {
           textAlloc = reqBlockSize(textlen);
           text = wrealloc(text, textAlloc);
         }
         text[textlen++] = c;
       }
     }   
     p = c;
  }     

      
  if(tokenlen>0) {  
    token[tokenlen] = 0;
    parseToken(token);
  }
  

  if(textlen>0) 
    insertText (text, textlen);

  if(text)
    wfree(text);

  if(token)
    wfree(token);
}
    


char *
cleanStream(char *dirtyStream)
{
  char *cleanStream, *ptr;

  if(!dirtyStream)
    return NULL;

  cleanStream = wstrdup(dirtyStream);
  ptr = cleanStream;

  while(*ptr) {
    if(*ptr == '\r') { 
      *ptr = ' ';
      if(ptr-cleanStream>0) {
        if(*(ptr-1) == '=')
          *(ptr-1) = '=';
      }
    }
    ptr++;
  }

  return cleanStream;
}



void HTMLParser(WMWidget *w, void *vstream)
{
  char *stream = cleanStream((char *)vstream);
  WMText *tPtr = (WMText *)w;
  static int recurse = 0;
  
  if(!tPtr || !stream)
    return;

  if(recurse) {
    printf("BLARGH: recursive!! replace with WMAppendTextBlock\n");
    return;
  }
  recurse = 1;

  Parser.tPtr = tPtr;
  Parser.colors = WMCreateArrayWithDestructor(4, releaseColors);
  Parser.fonts = WMCreateArrayWithDestructor(4, releaseFonts);
  Parser.align = WALeft;
  Parser.u = 0;
  Parser.first = 0;
  Parser.indent = 0;
  Parser.prepend = WMGetTextInsertType(tPtr);
  Parser.plainText = 1;
  Parser.tokenClosed = 1;
  Parser.escaping = 0;

  Parser.cfont = WMRetainFont(WMGetTextDefaultFont(tPtr));
  Parser.ccolor =  WMRetainColor(WMGetTextDefaultColor(tPtr));

  if(Aileron.textIsPlain || WMGetTextUsesMonoFont(tPtr)) {
    char *start, *mark;
    
    Parser.first = 1;
    start = stream; 
    while (start) {
      mark = strchr(start, '\n');
      if (mark) {
        insertText (start, (int) (mark-start));
        start = mark+1;
        Parser.first = 1;
       } else {
        if (start && strlen(start)) {
          insertText(start, strlen(start));
        } 
        start = mark;
      }
    }

  } else {
    parse(stream);
  }
  
  WMFreeArray(Parser.colors);
  WMFreeArray(Parser.fonts);
  WMReleaseFont(Parser.cfont);
  WMReleaseColor(Parser.ccolor);

  wfree(stream);
  recurse = 0;
}


