/******************************************************************************
 * program:     wp2latex                                                      *
 * function:    convert WordPerfect and other text documents into LaTeX       *
 * modul:       pass2.cc                                                      *
 * description: This modul contains functions for second pass. In the second  *
 *              pass the text file stripped from the WP binary file will be   *
 *              completed with the information of the binary table file which *
 *              contains additional information about environments.           *
 * licency:     GPL                                                           *
 ******************************************************************************/

//Ukoly: podivat se na: \\justified \\raggedright

#include <string.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>

#include "stringa.h"
#include "lists.h"
#include "wp2latex.h"
#include "struct.h"

#include "images/img_futi.h"


extern list Counters, UserLists;


//#define DEBUG_STOP_TEXT "xxxxx"

#ifdef DEBUG_STOP_TEXT
 bool debug_stop = false;
 #define DEBUG_CHECK if(debug_stop) __asm int 3;   
#else
 #define DEBUG_CHECK /* void */
#endif


#define TABPOS_COUNT	40

const unsigned char UTF8Head[3] = {0xEF, 0xBB, 0xBF};

/// Attribute for each line of conferted text.
class TLinePass2
{
public:
  string Lstring;			///< Text on the line.
  bool new_tabset;
  int tabent_num_of_tabs;
  char envir, line_term;
  char Columns;
  signed char Empty;
  unsigned char LineFlag;

  attribute attr;			///< Attributes opened at the beginning of this line.

  bool InhibitBsBslash;

  int ind_leftmargin;
  signed char Indent_Level;
  uint16_t tabent_tabpos[TABPOS_COUNT];
};


/// Main object that packs all needed stuff for pass2.
class TconvertedPass2
{
public:
  TconvertedPass2() {table=Strip=LaTeX=log=err=NULL;}

  Percentor perc;
  long rownum;
  uint8_t LangCount;

  TLinePass2 Lines[4];
  uint16_t tabpos[TABPOS_COUNT];  

  bool underline, illegal_argument, just_envir_closed;

  int num_of_tabs;
  char Col2Break;			///< Amount of \columnbreak inside multicol environment  
  
  short int pre, cur, next, next2;
  FILE *table;
  FILE *Strip, *LaTeX;
  FILE *log, *err;
  unsigned int  LineSpacing;

  void ReadTableEntry(short n);
  void LatexHead(void);
  void UsePackage(const char *PkgName);
  void Latex_foot(void);
  void Latex_tabset(void);
  void MoveCurlyBrace(void);
  bool Change_envir_BOL(void);
  bool Change_envir_EOL(void);
  void FixItallicAlignment(string *aLineStr);
  void CloseEnvironment(void);
  bool OpenEnvironment(void);
  void Update_global_information(void);
  void Select_NextLine(void);
  bool FixTabs(char *Helps);
};


static bool SectionOrEmpty(const char *str)
{
  int i=0, s=0;

  if(str == NULL) return true;

  while(str[i]!=0)
    {
    switch(s)
      {
      case 0: if(isspace((unsigned char)str[i]) || str[i]=='}') break;
              if(str[i]=='\\') {s=1; break;}
              return false;
      case 1: if(str[i]=='s') {s=2; break;}
              if(str[i]=='t') {s=21; break;}
              return false;
      case 2: if(str[i]=='u') {s=3; break;}
              if(str[i]=='e') {s=6; break;}
              return false;
      case 3: if(str[i]=='b') {s=4; break;}
              return false;
      case 4: if(str[i]=='s') {s=5; break;}
              return false;
      case 5: if(str[i]=='e') {s=6; break;}
              return false;
      case 6: if(str[i]=='c') {s=7; break;}
              return false;
      case 7: if(str[i]=='t') {s=8; break;}
              return false;
      case 8: if(str[i]=='i') {s=9; break;}
              return false;
      case 9: if(str[i]=='o') {s=10; break;}
              return false;
      case 10:if(str[i]=='n') {s=11; break;}
              return false;
      case 11:if(str[i]=='{')			// \subsection{}
                {
                int level = 0;
                while(str[i+1]!='}' || level>0)
  	          {
                  switch(str[i+1])
                    {
	            case 0:   return false;
                    case '{': level++; break;
                    case '}': level--; break;
                    }
                  i++;
                  }
                i++;
                s=0; break;
                }
              return false;

      case 21: if(str[i]=='a') {s=22; break;}	// \tableofcontents
               return false;
      case 22: if(str[i]=='b') {s=23; break;}
               return false;
      case 23: if(str[i]=='l') {s=24; break;}
               return false;
      case 24: if(str[i]=='e') {s=25; break;}
               return false;
      case 25: if(str[i]=='o') {s=26; break;}
               return false;
      case 26: if(str[i]=='f') {s=27; break;}
               return false;
      case 27: if(str[i]=='c') {s=28; break;}
               return false;
      case 28: if(str[i]=='o') {s=29; break;}
               return false;
      case 29: if(str[i]=='n') {s=30; break;}
               return false;
      case 30: if(str[i]=='t') {s=31; break;}
               return false;
      case 31: if(str[i]=='e') {s=32; break;}
               return false;
      case 32: if(str[i]=='n') {s=33; break;}
               return false;
      case 33: if(str[i]=='t') {s=34; break;}
               return false;
      case 34: if(str[i]=='s') {s=0; break;}
               return false;
      }
    i++;
    }
  return true;
}


void TconvertedPass2::ReadTableEntry(short n)
{
#ifdef DEBUG
  fprintf(log,"\n#Read_TableEntry(%d) ",(int)n); fflush(log);
#endif
uint16_t w;
uint8_t b;
int j;
TLinePass2 *pCurLine;

  pCurLine = &Lines[n];	// Begin met one schone lei die dan door this procedure verder wordt opgevuld.
  pCurLine->envir = ' ';
  pCurLine->LineFlag = 0;
  pCurLine->new_tabset = false;

  pCurLine->attr.InitAttr();

  if(feof(table)) return;

  b = 0;
  while(b != 0xff)
    {
    if(fread(&b, 1, 1, table)==0) // We are probaly at the end of the file
		break;

    switch(b)
	{
	case 'B':pCurLine->envir = 'B';    break;
	case 'C':pCurLine->envir = 'C';    break;
	case 'L':pCurLine->envir = 'L';    break;
	case 'R':pCurLine->envir = 'R';    break;
	case 'T':pCurLine->envir = 'T';    break;
	case 'q':pCurLine->envir = 'q';    break;
	case 'Q':pCurLine->envir = 'Q';    break;
	case 'I':
	case 'i':pCurLine->envir = b;
		 Rd_word(table, &w);
		 pCurLine->ind_leftmargin = w;
		 fread(&pCurLine->Indent_Level, 1, 1, table);
		 break;

	case 'S':pCurLine->new_tabset = true;
		 fread(&b, 1, 1, table);
		 pCurLine->tabent_num_of_tabs = b;
		 for(j=0; j<b; j++)
		     {
		     Rd_word(table, &w);
		     if(j<TABPOS_COUNT)
		       pCurLine->tabent_tabpos[j] = w;
		     }
                 if(b>=TABPOS_COUNT)
                   {
                   if(err!=NULL)
		     fprintf(err,_("\nError: tabent_num_of_tabs %d >= TABPOS_COUNT %d!"), pCurLine->tabent_num_of_tabs, TABPOS_COUNT);
                   pCurLine->tabent_num_of_tabs = TABPOS_COUNT;
                   }
		 break;

       case 'l': Rd_word(table, &w);
		 if(w!=LineSpacing)
			    {
			    fprintf(LaTeX,"\\baselineskip=%2.2fex ",(float)(w)/128.0);
			    LineSpacing=w;
			    }
		  break;

	case 'M':fread(&pCurLine->Columns, 1, 1, table);	break;

	case 'h':
	case 's':
	case 'P':
	case 'p':pCurLine->line_term = b;
		 break;

	case '%':pCurLine->LineFlag |= 1;  break; /* Useless comment line */

	case '!':pCurLine->envir = '!';    break; /* Ignore enviroment before */
	case '^':pCurLine->envir = '^';    break; /* Ignore enviroment after */

		    // Read all opened attributes into attr structure
	case 0xF0:if(fread(&b, 1, 1, table) != 1) 
		    {b=0xFF;break;}
		  pCurLine->attr.Opened_Depth = pCurLine->attr.Closed_Depth = b;
		  for(j=0; j<b; j++)
                  {
                    if(j < pCurLine->attr.getStackLimit())
		      fread(&(pCurLine->attr.stack[j]), 1, 1, table);
                    else
                      fseek(table,1,SEEK_CUR);
                  }
		  b=0xF0;
		  break;

	case 0xFF:
	case 10:
	case 13:break;               //do nothing

	default:if(err!=NULL)
		    fprintf(err,_("\nError: Bad command '%c' in table file!"),b);
	}
  }
}


void TconvertedPass2::UsePackage(const char *PkgName)
{
  fprintf(LaTeX, "\\usepackage{%s}\n", PkgName);
}


/** Make the standard-heading for one latex-file. */
void TconvertedPass2::LatexHead(void)
{
#ifdef DEBUG
  fprintf(this->log,"\n#Latex_head() ");fflush(this->log);
#endif
int i;
string TeX_RelativeFigDir;

  if(OutCodePage==UTF8)
      fwrite(UTF8Head,1,3,LaTeX);
  fprintf(LaTeX, _("%% This file has been created by the WP2LaTeX program version: %s \n"), VersionWP2L );
  if(LaTeX_Version<0x300)
    {
    fprintf(LaTeX, "\\documentstyle[11pt,");
    if(Accents>=1)  fprintf(this->LaTeX, "accents,");
    if(Amssymb>=1)  fprintf(this->LaTeX, "amssymb,");
    if(Arevmath>=1)  fprintf(this->LaTeX, "arevmath,");
    if(AmsMath>=1)   fprintf(this->LaTeX, "amsmath,");
    if(MakeIdx>=1)  fprintf(this->LaTeX, "makeidx,");
    if(Ulem>=1)     fprintf(this->LaTeX, "ulem,");
    if(Rotate>=1)   fprintf(this->LaTeX, "rotate,");
    if(Scalerel>=1) fprintf(this->LaTeX, "scalerel,");
    if(LineNo>=1)   fprintf(this->LaTeX, "lineno,");
    fprintf(this->LaTeX, "wp2latex");
    if(twoside)     fprintf(this->LaTeX, ",twoside");
    if(Columns==2)  fprintf(this->LaTeX, ",twocolumn");
    if(Columns>2)   fprintf(this->LaTeX, ",multicol");
    if(EndNotes>=1) fprintf(this->LaTeX, ",endnotes");
    if(arabtex>=1)  fprintf(this->LaTeX, ",arabtext");
    if(Cyrillic>=1) fprintf(this->LaTeX, ",cyrillic");
    if(cjHebrew>=1) fprintf(this->LaTeX, ",cjhebrew");
    if(mxedruli>=1) fprintf(this->LaTeX, ",mxedruli");
    if(InputPS==(IMG_InputPS|1))  fprintf(this->LaTeX, ",InputPS");
    if(InputPS==(IMG_graphicx|1)) fprintf(this->LaTeX, ",graphicx");
    if(InputPS==(IMG_epsfig|1))   fprintf(this->LaTeX, ",epsfig");
    if(InputPS==(IMG_graphics|1)) fprintf(this->LaTeX, ",graphics");
    if(Wasy>=1)     fprintf(this->LaTeX, ",wasyfont");
    if(textcomp>=1) fprintf(this->LaTeX, ",textcomp");
    if(Bbm>=1)      fprintf(this->LaTeX, ",bbm");
    if(Rsfs>=1)     fprintf(this->LaTeX, ",mathrsfs");
    if(FancyHdr>=1) fprintf(this->LaTeX, ",fancyhdr");
    if(LongTable>=1)fprintf(this->LaTeX, ",longtable");
    if(pifont>=1)   fprintf(this->LaTeX, ",pifont");
    if(enumitem>=1) fprintf(this->LaTeX, ",enumitem");
    for(int lng=0;lng<sizeof(LangTable)/sizeof(LangItem);lng++)
    {
      if(LangTable[lng].UseLang>=1)
	{
	fprintf(this->LaTeX, ",%s",LangTable[lng].LangDesc);
	this->LangCount++;
	}
    }
    if(NewUnicodeChar>=1 && LaTeX_Version<0x400) fprintf(this->LaTeX, ",newunicodechar");
    fprintf(this->LaTeX, "]{article}\n\n");
  }
  else
    {
    fprintf(this->LaTeX, "\\documentclass[11pt");
    if(twoside)     fprintf(this->LaTeX, ",twoside");
    if(Columns==2)  fprintf(this->LaTeX, ",twocolumn");
    fprintf(this->LaTeX, "]{article}\n");
    if(Accents>=1)  UsePackage("accents");
    if(Amssymb>=1)  UsePackage("amssymb");
    if(AmsMath>=1)  UsePackage("amsmath");
    if(Arevmath>=1) UsePackage("arevmath");
    if(LaTeXsym>=1) UsePackage("latexsym");
    if(MakeIdx>=1)  UsePackage("makeidx");
    if(Ulem>=1)     UsePackage("ulem");
    if(Rotate>=1)   UsePackage("rotate");
    if(Scalerel>=1) UsePackage("scalerel");
    if(LineNo>=1)   UsePackage("lineno");
    UsePackage("wp2latex");
    if(CPStyles) switch(OutCodePage)
	{
	case 0: break;
	case ISO8859_1:fprintf(LaTeX, "\\usepackage[latin1]{inputenc}\n");
		       break;
	case ISO8859_2:fprintf(LaTeX, "\\usepackage[latin2]{inputenc}\n");
		       break;
	case KOI8R:    fprintf(LaTeX, "\\usepackage[koi8-r]{inputenc}\n");
		       break;
        case UTF8:     fprintf(LaTeX, "\\usepackage[utf8x]{inputenc}\n");
                       fprintf(LaTeX, "\\usepackage[T1,T2A]{fontenc}\n");
		       break;
	default:fprintf(this->LaTeX, "\\usepackage[cp%d]{inputenc}\n", OutCodePage);
		break;
	}
    if(colors>=1)   UsePackage("color");
    if(arabtex>=1)  UsePackage("arabtex");
    if(Cyrillic>=1) UsePackage("cyrillic");
    if(cjHebrew>=1) UsePackage("cjhebrew");
    if(mxedruli>=1) UsePackage("mxedruli");
    if(EndNotes>=1) UsePackage("endnotes");
    if(InputPS==(IMG_InputPS|1))  UsePackage("InputPS");
    if(InputPS==(IMG_graphicx|1)) UsePackage("graphicx");
    if(InputPS==(IMG_epsfig|1))   UsePackage("epsfig");
    if(InputPS==(IMG_graphics|1)) UsePackage("graphics");
    if(Columns>2)   UsePackage("multicol");
    if(Wasy>=1)     UsePackage("wasyfont");
    if(textcomp>=1) UsePackage("textcomp");
    if(Bbm>=1)      UsePackage("bbm");
    if(TIPA>=1)     fprintf(this->LaTeX, "\\usepackage[safe]{tipa}\n");
    if(Rsfs>=1)     UsePackage("mathrsfs");
    if(FancyHdr>=1) UsePackage("fancyhdr");
    if(pifont>=1)   UsePackage("pifont");
    if(LongTable>=1)UsePackage("longtable");
    if(enumitem>=1) UsePackage("enumitem");
    
    for(int lng=0;lng<sizeof(LangTable)/sizeof(LangItem);lng++)
      {
      if(LangTable[lng].UseLang>=1)
	{
        if(this->LangCount==0)
          fprintf(this->LaTeX, "\\usepackage[%s",LangTable[lng].LangDesc);
        else
	  fprintf(this->LaTeX, ",%s",LangTable[lng].LangDesc);
	this->LangCount++;
	}     
      }
    if(this->LangCount>0)
      fprintf(this->LaTeX, "]{babel}\n");

    if(fontenc>=1)  switch(OutCodePage)
			{
			case 866:
			case KOI8R:
				 fprintf(this->LaTeX, "\\usepackage[T2A]{fontenc}\n");
				 break;
			default: fprintf(this->LaTeX, "\\usepackage[T1]{fontenc}\n");
			}

    fputc('\n',this->LaTeX);
  }
  if(NewUnicodeChar>=1 && LaTeX_Version<0x400) UsePackage("newunicodechar");

/*if (Cyrillic) fprintf(this->LaTeX, "\\font\\cyr=wncyr10\n");*/
  if(Columns==2)fprintf(this->LaTeX, "\\onecolumn\n");
  if (Index)    fprintf(this->LaTeX, "\\makeindex\n");
  if(FancyHdr>=1) fprintf(this->LaTeX, "\\pagestyle{fancy}\\renewcommand{\\headrulewidth}{0pt}\n\\fancyfoot{}\\fancyhead{}\n");
  if((InputPS&1)==1)
  {
    if(AbsolutePath(RelativeFigDir()) && !strncmp(OutputDir(),RelativeFigDir(),OutputDir.length()))
    {
      TeX_RelativeFigDir = "./";
      TeX_RelativeFigDir += RelativeFigDir()+ OutputDir.length();
      TeX_RelativeFigDir = replacesubstring(TeX_RelativeFigDir,"\\","/");
    }
    else
        TeX_RelativeFigDir = replacesubstring(RelativeFigDir,"\\","/");
  }
  if(InputPS==(IMG_InputPS|1) || InputPS==(IMG_epsfig|1) || InputPS==(IMG_graphics|1) || InputPS==(IMG_graphicx|1))
       fprintf(this->LaTeX, "\\newcommand{\\FigDir}{%s}\n",TeX_RelativeFigDir());

  if(InputPS==(IMG_InputPS|1))
	fprintf(this->LaTeX, "\\ShowDisplacementBoxes\n");
  if(InputPS==(IMG_graphicx|1))
	fprintf(this->LaTeX, "\\graphicspath{{%s/}}\n",TeX_RelativeFigDir());


  fputc('\n',this->LaTeX);
  if(EndNotes<-1) fprintf(this->LaTeX, "\\def\\endnote{\\footnote}\n");
  if(length(Counters)>0)
	{
	for(i=0;i<length(Counters);i++)
	    {
	    fprintf(this->LaTeX, "\\newcounter{%s}\n",chk(Counters[i]));
	    }
	fputc('\n',this->LaTeX);
	}


  for(i=0;i<length(UserLists);i++)
	{
	fprintf(this->LaTeX, "\\InitUserList{l%c}  %%%s\n",i+'a',UserLists[i]);
	}
  if(length(UserLists)>0) fputc('\n',this->LaTeX);

  fprintf(this->LaTeX, "\\begin{document}\n");
}


/** Sluit the latex-file op the juiste wijze af */
void TconvertedPass2::Latex_foot(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Latex_foot() ");fflush(log);
#endif
  if(EndNotes==1) fprintf(LaTeX,
  	"\\begingroup\n"
        "\\parindent 0pt \\parskip 1ex\n"
	"\\def\\enotesize{\\normalsize}\n"
	"\\theendnotes\n"
	"\\endgroup\n\n");

  fprintf(LaTeX, "\\end{document}\n");
}


void TconvertedPass2::Latex_tabset(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Latex_tabset() ");fflush(log);
#endif

  long atpr = 0;
  long j;
  double l;
  double ol = 0.0;
  long FORLIM;

	// Huiding number of tabs per row
  FORLIM = num_of_tabs;
  for(j=0; j<FORLIM; j++)
     {
     l = (double)(tabpos[j]) / 1200.0 * 2.54;
     fprintf(LaTeX, "\\hspace{%3.2fcm}\\=", l - ol);
     atpr++;
     if (atpr >= 4)
       {
       fprintf(LaTeX, "%%\n");
       atpr = 0;
       }
     ol = l;
     }
  fprintf(LaTeX, "\\kill\n");
}


bool TconvertedPass2::Change_envir_BOL(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Change_envir_BOL() ");fflush(log);
#endif

  return(Lines[cur].envir != Lines[pre].envir &&
	  (Lines[cur].envir == 'q' ||
	   Lines[cur].envir == 'Q' ||
	   Lines[cur].envir == 'T' ||
	   Lines[cur].envir == 'C' ||
	   Lines[cur].envir == 'R' ||
	   Lines[cur].envir == 'L' ||
	   Lines[cur].envir == 'B' ||
	   toupper(Lines[cur].envir) == 'I' ) &&

	   Lines[pre].envir != '^');
//	   && Lines[cur].envir != '!'
}


/** This function only checks whether environment is changed at end of line. */
bool TconvertedPass2::Change_envir_EOL(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Change_envir_EOL() ");fflush(log);
#endif
  const char NextEnvir = Lines[next].envir;
  const char CurrEnvir = Lines[cur].envir;

  return (NextEnvir != CurrEnvir &&
	  (CurrEnvir == 'Q' ||
	   CurrEnvir == 'q' ||
	   toupper(CurrEnvir) == 'I' ||
	   CurrEnvir == 'T' ||
	   CurrEnvir == 'C' ||
	   CurrEnvir == 'R' ||
	   CurrEnvir == 'L' ||
	   CurrEnvir == 'B' ||
	   NextEnvir == 'Q' ||
	   NextEnvir == 'q' ||
	   toupper(NextEnvir) == 'I' ||
	   NextEnvir == 'T' ||
	   NextEnvir == 'C' ||
	   NextEnvir == 'R' ||
	   NextEnvir == 'L' ||
	   NextEnvir == 'B') &&

	   NextEnvir != '!' &&
	   CurrEnvir != '^');
}


void TconvertedPass2::CloseEnvironment(void)
{
#ifdef DEBUG
  fprintf(log,"\n#CloseEnvironment() ");fflush(log);
#endif

 if(just_envir_closed) return;

 switch (Lines[cur].envir)
   {
   case 'B':if(LongTable>=1) fprintf(LaTeX, "\\end{longtable}\n");
	 	        else fprintf(LaTeX, "\\end{tabular}\n");
           break;

  case 'C':fprintf(LaTeX, "\\end{center}\n");
	   break;

  case 'L':fprintf(LaTeX, "\\end{flushleft}\n");
           break;

  case 'R':fprintf(LaTeX, "\\end{flushright}\n");
           break;

  case 'T':fprintf(LaTeX, "\\end{tabbing}\n");
	   break;

  case 'I':
  case 'i':fprintf(LaTeX, "\\end{indenting}\n");
           break;

  case 'q':fprintf(LaTeX, "\\end{eqnarray}\n");
           break;
  case 'Q':fprintf(LaTeX, "\\end{displaymath}\n");
           break;
  }

  just_envir_closed = true;
}


/// @return	false - no special handling; true - needs special handling like empty indent.
bool TconvertedPass2::OpenEnvironment(void)
{
#ifdef DEBUG
  fprintf(log,"\n#OpenEnvironment() ");fflush(log);
#endif
  string s;

  //fprintf(LaTeX, "ENV(%c-%c)", Lines[cur].envir, Lines[next].envir);

  if(Lines[cur].Columns!=Lines[pre].Columns)
	{
	if(Lines[cur].attr.Opened_Depth>0)	 // Attributes are opened.
	    {
	    Close_All_Attr(Lines[cur].attr,s);
	    Lines[cur].Lstring = s+Lines[cur].Lstring;
	    }
	CloseEnvironment();	//Close enviroment if still opened

	if(Columns>=3)	//style Multicol used
	  {
	  if(Lines[pre].Columns>1) fprintf(LaTeX,"\\end{multicols}\n");
	  if(Lines[cur].Columns>1)
	    {
	    fprintf(LaTeX,"\\begin{multicols}{%d}\n",(int)Lines[cur].Columns);
	    Col2Break=1;
	    }
	  }
	else
	  {
	  switch(Lines[cur].Columns)
	     {
	     case 0:
	     case 1:fprintf(LaTeX, "\\onecolumn\n"); break;
	     case 2:fprintf(LaTeX, "\\twocolumn\n"); break;
	     default:if(err!=NULL)
			fprintf(err,_("\nError: Bad number of columns: %d!"),(int)Lines[cur].Columns);
	     }
	  }
	}
  else
    {
    if(!Change_envir_BOL())  /* Changed environment? */
      {
      if (Lines[cur].new_tabset && Lines[cur].envir == 'T')
		Latex_tabset();
      if(Lines[cur].envir != ' ' && Lines[pre].envir == '^')
		just_envir_closed = false;  //mark popped enviroment as open
//      if(!just_envir_closed)
      return false;
      }
    }

  if (Lines[cur].attr.Opened_Depth>0)	 /* Attributes are opened */
	{
	Close_All_Attr(Lines[cur].attr,s);
	Lines[cur].Lstring = s + Lines[cur].Lstring;
	}

  just_envir_closed = false;
  switch(Lines[cur].envir)
    {
    case 'B':if(LongTable>=1) fprintf(LaTeX, "\\begin{longtable}\n");
			 else fprintf(LaTeX, "\\begin{tabular}\n");
	     break;

    case 'C':fprintf(LaTeX, "\\begin{center}\n");
	     break;

    case 'L':fprintf(LaTeX, "\\begin{flushleft}\n");
	     break;

    case 'R':fprintf(LaTeX, "\\begin{flushright}\n");
	     break;

    case 'T':fprintf(LaTeX, "\\begin{tabbing}\n");
	     Latex_tabset();
	     break;

    case 'I':
    case 'i':if(Lines[cur].Lstring.isEmpty())  //indent nothing ??
		 {
		 return true;		// Signalise special handling to the caller.
                 }
	     if((Lines[cur].Indent_Level<2)||(toupper(Lines[pre].envir)!='I'))
		 {
		 if(!Lines[pre].Lstring.isEmpty())
			   fprintf(LaTeX,"\\testlastline\n\n");
                 else fprintf(LaTeX,"\\zerotestlastline\n");
                 }
             fprintf(LaTeX, "\\begin{indenting}"
                                 "{%3.2fcm}\n",Lines[cur].ind_leftmargin / 1200.0 * 2.54);
             break;

    case 'q':fprintf(LaTeX, "\\begin{eqnarray}\n");
	     break;
    case 'Q':fprintf(LaTeX, "\\begin{displaymath}\n");
	     break;
    }

return false;
}


void TconvertedPass2::Update_global_information(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Update_global_information() ");fflush(log);
#endif
int j;
const TLinePass2 *pCurLine = &Lines[cur];

  if(pCurLine->new_tabset)
    {
    num_of_tabs = pCurLine->tabent_num_of_tabs;
    if(num_of_tabs > TABPOS_COUNT)
    {
      num_of_tabs = Lines[cur].tabent_num_of_tabs = TABPOS_COUNT;	// fix a problem.
    }
    for(j=0; j<num_of_tabs; j++)
          tabpos[j] = pCurLine->tabent_tabpos[j];
    }
}


void TconvertedPass2::Select_NextLine(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Select_NextLine() ");fflush(log);
#endif
  int help;

  help = pre;
  pre = cur;
  cur = next;
  next= next2;
  next2 = help;
}


bool TconvertedPass2::FixTabs(char *Helps)
{
  int i, j = 0, tabs = 0;
  string s;

  if(Helps == NULL) return false;
  if(*Helps == 0) return false;

  while(Helps[j++]=='\\')
  	{
	if(Helps[j++] != 'T') break;
	if(Helps[j++] != 'A') break;
	if(Helps[j++] != 'B') break;
	if(Helps[j] != 0)
          {
	  if(Helps[j++] != ' ') break;
          }
        tabs++;
        }
  if(tabs <= 0) return false;

  if((j>(int)strlen(Helps))&&(Lines[cur].line_term!='s')&&(Lines[cur].line_term!='p'))
  	{
	if(err!=NULL && Verbosing>0)
	   {
	   perc.Hide();
	   fprintf(err,_("\nWarning: Incorrect line with TABs at the end - fixed."));
	   }
	*Helps=0;
        return false;
        }


  if(Lines[cur].envir == 'T')
     {
     j=0;
     for(i=0; i<tabs; i++)
  	   {
	   Helps[j++]='\\';
	   Helps[j++]='>';
           }
     i = 5*tabs;
     while(Helps[i] != '\0')
            {
	    Helps[j] = Helps[i];
	    i++;
	    j++;
	    }
     Helps[j] = '\0';
     }
  else {
       i = Helps - Lines[cur].Lstring();
       s = copy(Lines[cur].Lstring,0,i);
       s.cat_printf("\\hspace*{%2.2fcm}", (float)(tabpos[tabs-1])/ 470.0);
       s += copy(Lines[cur].Lstring,i + 5*tabs,length(Lines[cur].Lstring)- i - 5*tabs);
       Lines[cur].Lstring=s;
       }
  return true;
}


static bool Fix1SectionStr(char *aline, const char *opt)
{
char *str, *str2;
int optlen;
int r;

  if((str=StrStr(aline, opt)) == NULL) return false;
  optlen = strlen(opt);
	
  str2 = str+1;
  for(r=0;r<optlen-1;r++)
    {
    *str2++=' ';
    }

  str2 = str + optlen;
  r = 0;
  while(*str2!=0)
    {
    if(*str2=='{') r++;
    if(*str2=='}')
      if(r==0)
        {
        *str2=' ';
        //strcpy(str,str+optlen);		// Overlapping regions, strcpy is dangerous
        const unsigned B2Move = strlen(str+optlen);
        memmove(str,str+optlen,B2Move);
        str[B2Move]=0; // Overlapping regions, but terminating 0 must be added for memmove.
	return true;
	}
      else r--;
    str2++;
    }
  return false;
}


/// Remove several attributes delimited by curly braces.
static void OptimSectionStr(char *aline)
{
bool optimized;
int i;

  do {     
     if(aline == NULL) return;


     i = strlen(aline);
     while(aline[i]==' ')
	{
	aline[i--] = 0;
	}
     if(*aline == 0) return;

     //i still contains strlen(aline)
     if(aline[i-1] == '\\' && aline[i-2] == '\\')
	{
	aline[i-2] = '\0';
	i -= 2;
	}

     optimized =  Fix1SectionStr(aline, "{\\bfseries");
     optimized |= Fix1SectionStr(aline, "{\\bf");
     optimized |= Fix1SectionStr(aline, "{\\large");
     optimized |= Fix1SectionStr(aline, "{\\Large");
     optimized |= Fix1SectionStr(aline, "{\\LARGE");
  } while(optimized);
}


static int FixBracket(string & Line)
{
  if(Line.isEmpty()) return 0;

  Line="{}"+Line;

  return length(Line);
}


static char *TryToRemoveGeneric(char *Helps, const char *Generic)
{
  int i = 0, j;

  if (Helps == NULL) return NULL;
  if (Generic == NULL) return NULL;
  if (*Helps == 0) return NULL;
  if (*Generic == 0) return NULL;

  Helps = StrStr(Helps, Generic);
  if(Helps == NULL) return NULL;

  j = strlen(Generic);

  while (Helps[j] == ' ')
  	{
        j++;
        }
  if (Helps[j++] != '}') return Helps+1;

  while (Helps[j] != '\0')
         {
	 Helps[i] = Helps[j];
	 i++;
	 j++;
	 } 
  Helps[i] = '\0';
  return Helps;
}


static char *TryToRemoveSelectLanguage(char *Helps)
{
  int i = 0, j, k;

  if (Helps == NULL) return NULL;
  if (*Helps == 0) return NULL;
  Helps = StrStr(Helps, "\\selectlanguage{");
  if (Helps == NULL) return NULL;

  j = 16;				// strlen("\\selectlanguage{");
  while (Helps[j] != '}')
  	{
	if(Helps[j]==0) return Helps+1;
        j++;
        }
  if (Helps[j++] != '}') return Helps+1;

  k = j;
  while (Helps[j] == '~' || Helps[j] == ' ')
  	{
        j++;
        }

  if (Helps[j++] != '\\') return Helps+1;
  if (Helps[j++] != 's') return Helps+1;
  if (Helps[j++] != 'e') return Helps+1;
  if (Helps[j++] != 'l') return Helps+1;
  if (Helps[j++] != 'e') return Helps+1;
  if (Helps[j++] != 'c') return Helps+1;
  if (Helps[j++] != 't') return Helps+1;
  if (Helps[j++] != 'l') return Helps+1;
  if (Helps[j++] != 'a') return Helps+1;
  if (Helps[j++] != 'n') return Helps+1;
  if (Helps[j++] != 'g') return Helps+1;
  if (Helps[j++] != 'u') return Helps+1;
  if (Helps[j++] != 'a') return Helps+1;
  if (Helps[j++] != 'g') return Helps+1;
  if (Helps[j++] != 'e') return Helps+1;
  if (Helps[j++] != '{') return Helps+1;

  while (Helps[k] != '\0')
         {
	 Helps[i] = Helps[k];
	 k++;
	 i++;
	 }
  Helps[i] = '\0';
  return Helps;		//one command removed, try again next command
}


static char *TryToRemoveLineNumbering(char * Helps)
{
  int i = 0, j, k;

  if (Helps == NULL) return NULL;
  if (*Helps == 0) return NULL;
  Helps = StrStr(Helps, "\\begin{linenumbers}");
  if (Helps == NULL) return NULL;

  j = 19;				// strlen("\\begin{linenumbers}");
  k = j;

  while (Helps[j]==' ' || Helps[j]=='\t')
  	{
        j++;
        }
  
  if (Helps[j++] != '\\') return Helps+1;
  if (Helps[j++] != 'e') return Helps+1;
  if (Helps[j++] != 'n') return Helps+1;
  if (Helps[j++] != 'd') return Helps+1;
  if (Helps[j++] != '{') return Helps+1;
  if (Helps[j++] != 'l') return Helps+1;
  if (Helps[j++] != 'i') return Helps+1;
  if (Helps[j++] != 'n') return Helps+1;
  if (Helps[j++] != 'e') return Helps+1;
  if (Helps[j++] != 'n') return Helps+1;
  if (Helps[j++] != 'u') return Helps+1;
  if (Helps[j++] != 'm') return Helps+1;
  if (Helps[j++] != 'b') return Helps+1;
  if (Helps[j++] != 'e') return Helps+1;
  if (Helps[j++] != 'r') return Helps+1;
  if (Helps[j++] != 's') return Helps+1;
  if (Helps[j++] != '}') return Helps+1;

	// Copy spaces among linenum commands.
  while (Helps[k]!='\0' && Helps[k]!='\\')
	{
	Helps[i] = Helps[k];
	i++; k++;
	}    
  
	// Copy rest of a text.
  while (Helps[j] != '\0')
         {
	 Helps[i] = Helps[j];
	 i++;
	 j++;
	 } 
  Helps[i] = '\0';
  return Helps;		//one command removed, try again next command
}


/** Check whether line contains any user printable text, or control sequences only.
 * @return	1 when line contains sequences only; 0 printable text is present. */
int EmptyLine(const string & s)
{
int i;
char ch;

i=0;
while(i<length(s))
	{
        ch=s[i++];
        if(ch==' ') continue;
        if(ch=='\t') continue;
	if(ch=='}') continue;
	if(ch=='{') continue;
	if(ch=='\\')
	   {
	   if(s[i]=='/')	 //ignore \/
		 {
		 i++;
		 continue;
		 }
           if(s[i]=='>')	 //ignore \>
		 {
		 i++;
		 continue;
		 }

	   if(s[i]=='b')	 //ignore \bf
		 {
		 i++;
		 if(s[i++]!='f') return(0);
		 continue;
		 }

	   if(s[i]=='c')
		 {
		 i++;
		 if(s[i]=='l')	//ignore \clubpenalty number
		    {
		    i++;
		    if(s[i++]!='u') return(0);
		    if(s[i++]!='b') return(0);
		    if(s[i++]!='p') return(0);
		    if(s[i++]!='e') return(0);
		    if(s[i++]!='n') return(0);
		    if(s[i++]!='a') return(0);
		    if(s[i++]!='l') return(0);
		    if(s[i++]!='t') return(0);
		    if(s[i++]!='y') return(0);
		    while(s[i]==' ') i++;
		    while(isdigit(s[i])) i++;
		    continue;
		    }
		 if(s[i++]!='o') return(0); //ignore \color{\anything}
		 if(s[i++]!='l') return(0);
		 if(s[i++]!='o') return(0);
		 if(s[i++]!='r') return(0);

		 if(s[i++]!='{') return(0);
		 while(s[i++]!='}')
			 {
			 if(i>=length(s)) return(1);
			 };
		 continue;
		 }

	   if(s[i]=='f')	 //ignore \foottext{\anything}
		 {
		 i++;
		 if(s[i++]!='o') return(0);
		 if(s[i++]!='o') return(0);
		 if(s[i++]!='t') return(0);
		 if(s[i++]!='t') return(0);
		 if(s[i++]!='e') return(0);
		 if(s[i++]!='x') return(0);
		 if(s[i++]!='t') return(0);

		 if(s[i++]!='{') return(0);
		 while(s[i++]!='}')
			 {
			 if(i>=length(s)) return(1);
			 };
		 continue;
		 }


	   if(s[i]=='h')	 //ignore \headtext{\anything}
		 {
		 i++;
		 if(s[i]=='y')
		 {
		   i++;
		   if(s[i++]!='p') return(0);
		   if(s[i++]!='h') return(0);
		   if(s[i++]!='e') return(0);
		   if(s[i++]!='n') return(0);
		   if(s[i++]!='p') return(0);
		   if(s[i++]!='e') return(0);
		   if(s[i++]!='n') return(0);
		   if(s[i++]!='a') return(0);
		   if(s[i++]!='l') return(0);
		   if(s[i++]!='t') return(0);
		   if(s[i++]!='y') return(0);
		   while(isspace(s[i])) i++;
		   while(isdigit(s[i])) i++;
		   continue;
		 }
		 if(s[i++]!='e') return(0);
		 if(s[i++]!='a') return(0);
		 if(s[i++]!='d') return(0);
		 if(s[i++]!='t') return(0);
		 if(s[i++]!='e') return(0);
		 if(s[i++]!='x') return(0);
		 if(s[i++]!='t') return(0);

		 if(s[i++]!='{') return(0);
		 while(s[i++]!='}')
			 {
			 if(i>=length(s)) return(1);
			 };
		 continue;
		 }

	   if(s[i]=='i')	 //ignore \it
		 {
		 i++;
		 if(s[i++]!='t') return(0);
		 continue;
		 }

	   if(s[i]=='l')	 //ignore \label{anything}
		 {
		 i++;
		 if(s[i++]!='a') return(0);
		 if(s[i++]!='b') goto TryLanguage;
		 if(s[i++]!='e') return(0);
		 if(s[i++]!='l') return(0);
		 if(s[i++]!='{') return(0);
		 while(s[i++]!='}')
			 {
			 if(i>=length(s)) return(1);
			 };
		 continue;

TryLanguage:            	//ignore \language anything
		 i--;
		 if(s[i++]!='n') return(0);
		 if(s[i++]!='g') return(0);
		 if(s[i++]!='u') return(0);
		 if(s[i++]!='a') return(0);
		 if(s[i++]!='g') return(0);
		 if(s[i++]!='e') return(0);
		 if(s[i++]!='=') return(0);
		 if(s[i++]!='\\') return(0);

		 while(isalpha(s[i]))
			 {
			 i++;
			 }
		 continue;
		 };

	   if(s[i]=='o')	 //ignore  \onecolumn
		 {
		 i++;
		 if(s[i++]!='n') return(0);
		 if(s[i++]!='e') return(0);
		 goto ColumnText;
		 }

	   if(s[i]=='p')
		 {
		 i++;
                 if(s[i]=='a')		//ignore  \pagenumpos{...}
                   {
                   i++;
                   if(s[i++]!='g') return(0);
		   if(s[i++]!='e') return(0);
		   if(s[i++]!='n') return(0);
                   if(s[i++]!='u') return(0);
		   if(s[i++]!='m') return(0);
                   if(s[i++]!='p') return(0);
                   if(s[i++]!='o') return(0);
                   if(s[i++]!='s') return(0);
                   if(s[i++]!='{') return(0);
 		   while(s[i]!='}' && s[i]!=0)
                     {i++;}
 		   continue;
                   }
		 if(s[i++]!='e') return(0);	//ignore  \penalty NUM
		 if(s[i++]!='n') return(0);
		 if(s[i++]!='a') return(0);
                 if(s[i++]!='l') return(0);
		 if(s[i++]!='t') return(0);
                 if(s[i++]!='y') return(0);
	         while(s[i]==' ') i++;
	         while(isdigit(s[i])) i++;
                 continue;
	         };

	   if(s[i]=='s')
		 {
		 i++;
		 if(s[i]=='c') continue;	//ignore \sc

		 if(s[i++]!='e') return(0);  
                 if(s[i]=='l')
		   {				//ignore  \selectlanguage{lang}
		   i++;
		   if(s[i++]!='e') return(0);
                   if(s[i++]!='c') return(0);
  		   if(s[i++]!='t') return(0);
		   if(s[i++]!='l') return(0);
		   if(s[i++]!='a') return(0);
                   if(s[i++]!='n') return(0);
                   if(s[i++]!='g') return(0);
                   if(s[i++]!='u') return(0);
                   if(s[i++]!='a') return(0);
                   if(s[i++]!='g') return(0);
                   if(s[i++]!='e') return(0);
                   if(s[i++]!='{') return(0);
 		   while(s[i]!='}' && s[i]!=0) i++;
 		   continue;
		   }
		 
		 if(s[i++]!='t') return(0);	//ignore  \setcounter{counter}{number}
                 if(s[i++]!='c') return(0);
		 if(s[i++]!='o') return(0);
		 if(s[i++]!='u') return(0);
		 if(s[i++]!='n') return(0);
                 if(s[i++]!='t') return(0);
                 if(s[i++]!='e') return(0);
                 if(s[i++]!='r') return(0);

                 if(s[i++]!='{') return(0);
                 while(s[i]!='}' && s[i]!=0) i++;
                 if(s[++i]!='{') return(0);
                 while(s[i]!='}' && s[i]!=0) i++;
                 i++;

		 continue;
		 };

	   if(s[i]=='T')	 //ignore  \TAB
		 {
		 i++;
		 if(s[i++]!='A') return(0);
		 if(s[i++]!='B') return(0);
		 continue;
		 }

	   if(s[i]=='t')	 //ignore  \twocolumn
		 {
		 i++;
		 if(s[i]!='w') goto ThisPageStyle;
		 i++;
ColumnText:	 if(s[i++]!='o') return(0);
		 if(s[i++]!='c') return(0);
		 if(s[i++]!='o') return(0);
		 if(s[i++]!='l') return(0);
		 if(s[i++]!='u') return(0);
		 if(s[i++]!='m') return(0);
		 if(s[i++]!='n') return(0);
		 if(s[i]==0) return(1);
		 if(s[i++]!=' ') return(0);
		 continue;

ThisPageStyle:   i++;
		 if(s[i++]!='h') return(0);
		 if(s[i++]!='i') return(0);
		 if(s[i++]!='s') return(0);
		 if(s[i++]!='p') return(0);
		 if(s[i++]!='a') return(0);
		 if(s[i++]!='g') return(0);
		 if(s[i++]!='e') return(0);
		 if(s[i++]!='s') return(0);
		 if(s[i++]!='t') return(0);
		 if(s[i++]!='y') return(0);
		 if(s[i++]!='l') return(0);
		 if(s[i++]!='e') return(0);
		 if(s[i++]!='{') return(0);

		 while(isalpha(s[i]))
		       {
		       i++;
		       }
		 if(s[i++]!='}') return(0);
		 continue;
		 }

	   if(s[i]=='v')
	         {
                 i++;
                 if(s[i++]!='s') return(0);
		 switch(s[i++])
                   {
				//ignore  \vskip xxcm
		     case 'k': if(s[i++]!='i') return(0);
                               if(s[i++]!='p') return(0);
                               if(s[i++]!=' ') return(0);
                               while(s[i]==' ') i++;
                               while(isdigit(s[i]) || (s[i]=='-') || (s[i]=='.')) i++;
		               if(s[i]!='c') continue;
		               i++;
		               if(s[i]!='m') continue;
		               i++;
			       break;
				//ignore  \vspace {xx cm}
                     case 'p': if(s[i++]!='a') return(0);
                               if(s[i++]!='c') return(0);
			       if(s[i++]!='e') return(0);
			       while(s[i]==' ') i++;
			       if(s[i++]!='{') return(0);
			       while(s[i]!='}')
			         {
				 if(s[i]==0) return(1);
				 i++;
                                 }
			       break;
                     default:  return(0); 
                   }
                 continue;
	         };
	  if(s[i]=='w')	//ignore \widowpenalty number
		{
		i++;
		if(s[i++]!='i') return(0);
		if(s[i++]!='d') return(0);
		if(s[i++]!='o') return(0);
		if(s[i++]!='w') return(0);
 		if(s[i++]!='p') return(0);
 		if(s[i++]!='e') return(0);
 		if(s[i++]!='n') return(0);
 		if(s[i++]!='a') return(0);
 		if(s[i++]!='l') return(0);
 		if(s[i++]!='t') return(0);
 		if(s[i++]!='y') return(0);
 		while(s[i]==' ') i++;
 		while(isdigit(s[i])) i++;
 		continue;
		}
           }
        return(0);		//Line is not Empty
        }
return(1);			//Line is Empty
}


/** This function groups multiple rows inside one table cell into a \shortstack
 * @param[in,out]	aLine	Row string to be fixed. */
static int OptimizeMultiRows(string & aLine)
{
string NewLine,Cell;
int i;
const char *HelpS, *aLinePtr;
char OldChar;
int shortstack;

 HelpS = StrStr(aLine(), "\\penalty-10001");
 shortstack = 0;
 aLinePtr = aLine();
 OldChar = 0;
 while(*aLinePtr!=0)
	{
	if(aLinePtr==HelpS)
	     {
	     i=0;
	     HelpS = StrStr(++HelpS, "\\penalty-10001");
	     aLinePtr += strlen("\\penalty-10001");
	     while(aLinePtr[i]==' ')
		 {
		 i++;
		 if(aLinePtr[i]==0)   goto Reject;
		 if(aLinePtr[i]=='&') goto Reject;
		 if(aLinePtr[i]=='\\')
		   {
		   if(aLinePtr[++i]=='\\') goto Reject;
		   }		 
		 }

	     if(!shortstack) Cell="\\shortstack{"+Cell;
	     Cell+="\\\\";
	     if(aLinePtr[i]=='[') Cell+="{}";
			     else if(*aLinePtr!=' ') Cell+=' ';
	     shortstack=3;
	     }

Reject:	if(OldChar!='\\')
	{
	  if(*aLinePtr==0 || *aLinePtr=='&' || (*aLinePtr=='\\' && aLinePtr[1]=='\\'))
	     {
	     NewLine+=Cell;
	     if(shortstack)
		 {
		 if(shortstack>1) NewLine+="\\strut";
		 NewLine+='}';
		 }

	     NewLine += OldChar = *aLinePtr++;
	     Cell.erase();
	     shortstack=0;
	     continue;
	     }

          if(*aLinePtr=='$')		// Skip formula, formula is unbreakable.
	     {
	       do
	         {
	         Cell += OldChar = *aLinePtr++;	
	         } while(*aLinePtr!='$' && *aLinePtr!=0);
	       if(*aLinePtr ==0) break;
	       if(HelpS < aLinePtr)
	         {
	         HelpS = StrStr(aLinePtr, "\\penalty-10001");
		 }
	     }
        }

	if(*aLinePtr!=' ') shortstack&=~2;
	Cell += OldChar = *aLinePtr++;
	}

 NewLine+=Cell;
 if(shortstack)
   {
   if(shortstack>1) NewLine+="\\strut";
   NewLine+='}';
   }

 aLine = NewLine;
 return(shortstack);
}


/** fix occurence \/ in the vertical mode - remove it */
void TconvertedPass2::FixItallicAlignment(string *aLineStr)
{
  if( (Lines[pre].Lstring.isEmpty())&&(length(*aLineStr)>=2) )
    if (  (*aLineStr)[0]=='\\' &&  (*aLineStr)[1]=='/' )
	*aLineStr=copy(*aLineStr,2,length(*aLineStr)-2);

  if( (length(Lines[next].Lstring)>=2) &&
     (aLineStr->isEmpty() ||
       Lines[cur].line_term=='h' || Lines[cur].line_term=='p') )
    if( (Lines[next].Lstring)[0]=='\\' &&  (Lines[next].Lstring)[1]=='/' )
	Lines[next].Lstring=copy(Lines[next].Lstring,2,length(Lines[next].Lstring)-2);
}


/** move } from the beginning of next line to the current line, only when some attribute is opened. */
void TconvertedPass2::MoveCurlyBrace(void)
{
const char *CHelpS;
TLinePass2 *NextLine = &Lines[next];
TLinePass2 *CurLine = &Lines[cur];

  while(NextLine->attr.Opened_Depth>0)
  {
    CHelpS = NULL;
    
		// check whether curly brace belongs to a style opened
    if(NextLine->attr.Closed_Depth>0)
    {
      if(NextLine->attr.Opened_Depth >= NextLine->attr.Closed_Depth)
        {		// try to remove all string belonging to the attr
        CHelpS = StyleOpCl[NextLine->attr.stack[NextLine->attr.Opened_Depth-1]].Close;
MatchAgain:
        if(StrNCmp(NextLine->Lstring(), CHelpS, strlen(CHelpS)))
	  {
	  if(CHelpS[0]=='\\' && CHelpS[1]=='/') // give a last chance to match without \/
	    {
	    CHelpS += 2;
	    if(*CHelpS!=0) goto MatchAgain;
	    }
	  break;		// close command differs, break the loop
          }
        NextLine->attr.Opened_Depth--;
        }
      NextLine->attr.Closed_Depth--;
    }

    if(CHelpS==NULL)
    {
      if(NextLine->Lstring[0]!='}') break;
      CHelpS = "}";
    }
  
		// remove string from the next line
    NextLine->Lstring = copy(NextLine->Lstring,strlen(CHelpS),length(NextLine->Lstring)-strlen(CHelpS));

		// and append it to a current line    
    if(CurLine->Lstring.isEmpty() && !Lines[pre].Lstring.isEmpty())
        {	// When a current line is empty, add \n	
	CurLine->Lstring += CHelpS;
	CurLine->Lstring += "\n";
	}
    else
        CurLine->Lstring += CHelpS;
    }
}


static void BlockBslBslOnLinenum(TLinePass2 &CurrLine)
{
  if(CurrLine.Lstring.length()<17) return;  // sizeof("\\end{linenumbers}") = 17
  const char *Ending = StrStr(CurrLine.Lstring(), "\\end{linenumbers}");
  if(Ending==NULL) return;
  CurrLine.InhibitBsBslash = true;
}



static const char *GenericStr[] = 
	{"{\\bf", "{\\footnotesize", "{\\it",
	 "{\\large", "{\\Large", "{\\LARGE", "{\\rm", 
	 "{\\sc", "{\\small","{\\tt"};

static const char *SectionStr[] = 
	{"\\part{", "\\chapter{", 
         "\\section{", "\\subsection{", "\\subsubsection{", 
         "\\paragraph{", "\\subparagraph{"};


/*******************---PASS2---******************/
void Convert_second_pass(FILE *Strip, FILE *table, FILE *FileOut, FILE *log, FILE *ErrorFile)
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_second_pass() ");fflush(log);
#endif
int i, len_reg;
string *aLineStr, s;
char *HelpS;
TconvertedPass2 cq2;

  if(FileOut==NULL) return;

  cq2.table = table;
  cq2.Strip = Strip;
  cq2.LaTeX = FileOut;
  cq2.log   = log;
  cq2.err   = ErrorFile;

  cq2.pre = 0;
  cq2.cur = 1;
  cq2.next = 2;
  cq2.next2 = 3;
  cq2.LangCount = 0;

  cq2.Lines[cq2.pre].envir = ' ';
  cq2.Lines[cq2.pre].new_tabset = false;

  cq2.just_envir_closed = true;
  cq2.LineSpacing=0;
  cq2.Col2Break=1;

  for(i=0; i<=3; i++)
      {
      cq2.Lines[i].Lstring.erase();
      cq2.Lines[i].Empty   = true;
      cq2.Lines[i].Columns = 1;
      cq2.Lines[i].InhibitBsBslash = false;
      cq2.Lines[i].attr.InitAttr();
      cq2.Lines[i].line_term = 's';
      }

  cq2.rownum = 1;
  cq2.perc.Init(0, num_of_lines_stripfile, _("Second pass:"));

  cq2.LatexHead();
			// read current row from strip file.
  cq2.ReadTableEntry(cq2.cur);
  fGets2(Strip, cq2.Lines[cq2.cur].Lstring );
  cq2.Lines[cq2.cur].Empty = EmptyLine(cq2.Lines[cq2.cur].Lstring);
			// read next row from strip file.
  fGets2(Strip, cq2.Lines[cq2.next].Lstring);
  cq2.ReadTableEntry(cq2.next);
  cq2.Lines[cq2.next].Empty = EmptyLine(cq2.Lines[cq2.next].Lstring);
				/*read row after next row from strip file*/
  fGets2(Strip, cq2.Lines[cq2.next2].Lstring);
  cq2.ReadTableEntry(cq2.next2);
  cq2.Lines[cq2.next2].Empty = EmptyLine(cq2.Lines[cq2.next2].Lstring);


  while((!feof(Strip) || !cq2.Lines[cq2.cur].Lstring.isEmpty() || !cq2.Lines[cq2.next].Lstring.isEmpty() || !cq2.Lines[cq2.next2].Lstring.isEmpty()))
     {
     cq2.Update_global_information();     

     if(Verbosing >= 1)		// actualise a procentage counter
	      cq2.perc.Actualise(cq2.rownum);

     aLineStr = &cq2.Lines[cq2.cur].Lstring;

     if(TIPA>=2)	// Unsafe Tipa
       {
       *aLineStr = replacesubstring(*aLineStr,"\\!","\\!{}");
       }

     len_reg = length(*aLineStr);

	// Remove all language mess if only one language is present in a document.
     if(cq2.LangCount<2)
       {
       HelpS = StrStr((*aLineStr)(), "\\selectlanguage{");
       if(HelpS!=NULL)
         {
	 i = *HelpS;
	 *HelpS = 0;		// Temporary terminate.
	 s = (*aLineStr)();
	 *HelpS = i;		// Return original value.

	 HelpS+=16;

	 for(i=0; i<strlen(HelpS); i++)
	   {
	   if(HelpS[i]==0) break;
	   if(HelpS[i]=='}')		// remove everything till closing brace
	     {
	     s += HelpS+i+1;
	     *aLineStr = s;
             break;
	     }
	   }
	 }
       }

        /* Try to find and remove useless trailing TAB */
    if(cq2.Lines[cq2.next].line_term=='p' || cq2.Lines[cq2.next].line_term=='h')
        {
        int pos = -1;
        for(i=length(cq2.Lines[cq2.next].Lstring)-1; i>=1; i--)
            {
            unsigned char ch = (cq2.Lines[cq2.next].Lstring)[i];
            if(isspace(ch))
                {
                pos = i;
                continue;
                }
            if(ch=='>' && (cq2.Lines[cq2.next].Lstring)[i-1]=='\\')
                {
                pos = i - 1;
                i--;
                continue;
                }
            break;
            }
        if(pos>=0)
            {
            cq2.Lines[cq2.next].Lstring = copy(cq2.Lines[cq2.next].Lstring, 0, pos);
            }
        }

	// Fix an empty environments C and L.
     if(((cq2.Lines[cq2.cur].envir==' ')||(cq2.Lines[cq2.cur].envir=='Q')||(cq2.Lines[cq2.cur].envir=='B')) &&
	 (cq2.Lines[cq2.next].Empty>0) && (cq2.Lines[cq2.next2].envir!='!') )
	   {
	   if(cq2.Lines[cq2.next].envir == 'C' || cq2.Lines[cq2.next].envir == 'L')
		{
		cq2.Lines[cq2.next].envir = ' ';
		}
	   }

     if(cq2.Lines[cq2.cur].envir != cq2.Lines[cq2.next].envir)  // Environment is changed.
       {
       if((cq2.Lines[cq2.next].envir=='L' || cq2.Lines[cq2.next].envir=='T') &&
          cq2.Lines[cq2.next2].envir!='!' &&
          cq2.Lines[cq2.cur].envir!='^' &&
          SectionOrEmpty(cq2.Lines[cq2.next].Lstring()))
         {
         cq2.Lines[cq2.next].envir = ' ';
         }
      }

	// Fix tabs, which are not in the tabbing environment
    HelpS = StrStr((*aLineStr)(), "\\TAB");
    if(HelpS!=NULL)
      {
      if(HelpS[4]==' ' || HelpS[4]==0)
        {
	cq2.FixTabs(HelpS); /* Fix tabs must be called first because it can change environment */
	check(*aLineStr);
	}
      }
    if(cq2.Lines[cq2.cur].envir=='T')  /* fix wrongly accented chars in Tabbing enviroment */
	{
	if(StrStr((*aLineStr)(), "\\'{")!=NULL)
	   *aLineStr=replacesubstring(*aLineStr,"\\'{","\\a'{");
	if(StrStr((*aLineStr)(), "\\`{")!=NULL)
	   *aLineStr=replacesubstring(*aLineStr,"\\`{","\\a`{");
	}


    if(FixSpaces) {   /* More than two spaces must be fixed. */
      if(toupper(cq2.Lines[cq2.cur].envir) != 'Q') {
	for(i = 1; i < len_reg; i++)
	  {
	  if ((*aLineStr)[i] == ' ' && (*aLineStr)[i+1] == ' ')
	    {
	    (*aLineStr)[i+1] = '~';
	    }
	  }
      }
    }

    /* Correct illegal argument which begins '['. */
    if(len_reg >= 1 && (*aLineStr)[1] == '[' ||
	len_reg >= 2 && (*aLineStr)[1] == ' ' && (*aLineStr)[2] == '[') {
      len_reg = FixBracket(*aLineStr);
    }


	/* Try to repair \section{ */
    if(OptimizeSection)
      {
      for(i=0; i<sizeof(SectionStr)/sizeof(char*); i++)
        {
        char *sec_str = StrStr((*aLineStr)(), SectionStr[i]);
        if(sec_str!=NULL)
          {
          if(cq2.Lines[cq2.cur].envir=='T')
              cq2.Lines[cq2.cur].envir=' ';	// Tabbing environment cannot wrap around section.
	  cq2.Lines[cq2.cur].InhibitBsBslash = true;
//	  cq2.Lines[cq2.next].InhibitBsBslash = true;
	  OptimSectionStr(sec_str + strlen(SectionStr[i]));
	  check(*aLineStr);		// Fix proper string length after schrink.
          }
        }
       }


	/* Some optimizations of text on current line */

			//fix beginning paragraph with \raise command
    if(cq2.Lines[cq2.pre].Lstring.isEmpty() && length(*aLineStr)>=6)
	{
	if(!strncmp((*aLineStr),"\\raise",6))
		*aLineStr="\\strut"+*aLineStr;
	}

          // fix occurence \/ in the vertical mode - remove it
    cq2.FixItallicAlignment(aLineStr);

	  // move } from the beginning of next line to the current line, only when some attribute is opened.
    cq2.MoveCurlyBrace();

	//fix \/ again - moving } could free \/
    cq2.FixItallicAlignment(aLineStr);

    if(cq2.OpenEnvironment())    //this procedure tests change of the environment
	{
	i = cq2.cur;	//opening enviroment rejected for an empty line
	cq2.cur = cq2.next;
	cq2.next = cq2.next2;
	cq2.next2 = i;

	fGets2(Strip, cq2.Lines[cq2.next2].Lstring);
	cq2.Lines[cq2.next2].Columns = cq2.Lines[cq2.next].Columns;
	cq2.ReadTableEntry(cq2.next2);

	if((cq2.Lines[cq2.cur].envir=='i')&&(cq2.Lines[cq2.pre].envir=='i'))
			cq2.Lines[cq2.pre].envir='I';
        if((cq2.Lines[cq2.cur].envir=='I')&&(cq2.Lines[cq2.pre].envir=='I'))
			cq2.Lines[cq2.pre].envir='i';

        goto SkipThisLine;
        }


		//fix closed attributes problem
    if(cq2.Lines[cq2.cur].attr.ClosedAttr()) /* Are attributes closed? */
      if(aLineStr->isEmpty())
	 {
	 if(cq2.Lines[cq2.next].attr.Closed_Depth==cq2.Lines[cq2.cur].attr.Closed_Depth)
		{
		for(i=0;i<cq2.Lines[cq2.next].attr.Closed_Depth;i++)
                	{
                        if(cq2.Lines[cq2.next].attr.stack[i]!=cq2.Lines[cq2.cur].attr.stack[i])
				{	//attributes are not same for next line
                                goto FixOpen;
				}
			}
		cq2.Lines[cq2.next].attr.Opened_Depth=cq2.Lines[cq2.cur].attr.Opened_Depth;
		}
	     else {
FixOpen:	  Open_All_Attr(cq2.Lines[cq2.cur].attr,*aLineStr);
		  }
         }


  if(!aLineStr->isEmpty() || cq2.Lines[cq2.next].envir=='!')
	 {		/* Attributes are closed */
	 if(cq2.Lines[cq2.cur].LineFlag & 1  && (aLineStr->isEmpty()||*aLineStr=='%'))
		 {
		 if(cq2.Change_envir_EOL())
		   cq2.CloseEnvironment();
		 goto EmptyThisLine; /* Remove empty line added by wp2latex */
		 }

	 if(cq2.Lines[cq2.cur].attr.Closed_Depth > cq2.Lines[cq2.cur].attr.Opened_Depth)
		{
		Open_All_Attr(cq2.Lines[cq2.cur].attr,s);
		*aLineStr=s+*aLineStr;
		}

	 HelpS = StrStr((*aLineStr)(), "@{\\");   /*ignore tabular formater @{...}*/
	 if(HelpS==NULL)
	   {
	   for(i=0; i<sizeof(GenericStr)/sizeof(char*); i++)
	     {
	     HelpS = (*aLineStr)();
	     do {
	        HelpS = TryToRemoveGeneric(HelpS,GenericStr[i]);
	        } while(HelpS!=NULL);
             }
	   }

		/* remove duplicit items \selectlanguage */
	 HelpS = (*aLineStr)();
	 do {
	    HelpS = TryToRemoveSelectLanguage(HelpS);
	    } while(HelpS);

	 HelpS = (*aLineStr)();
	 do {
	    HelpS = TryToRemoveLineNumbering(HelpS);
	    } while(HelpS);
	   
	 check(*aLineStr);

	 if(cq2.Lines[cq2.cur].envir=='B')
		{
		HelpS = StrStr((*aLineStr)(), "\\penalty-10001");
		if(HelpS!=NULL)
		  {
		  if(OptimizeMultiRows(*aLineStr))
		    if(cq2.Lines[cq2.next].envir=='!' && cq2.Lines[cq2.next].Lstring=="%")
		       *aLineStr+="\\\\";
		  }
		}

	 if(length(*aLineStr)>0)
           {
	   if((i=fputs((*aLineStr)(),cq2.LaTeX))==EOF) /* Here is written the current line. */
			{
			RunError(0x20,"");
			}
           }
	 }

		// Is it possible to expand the tabbing enviroment?
    HelpS = StrStr(cq2.Lines[cq2.next].Lstring(), "\\TAB ");
    if((HelpS!=NULL)&&(cq2.Lines[cq2.cur].envir=='T')&&(cq2.Lines[cq2.next].envir==' '))
	{
	cq2.Lines[cq2.next].envir='T';  /* Tabbing expanded for next line */
	}

	// LaTex cannot process \\ on linenumbers
    BlockBslBslOnLinenum(cq2.Lines[cq2.cur]);

    switch(cq2.Lines[cq2.cur].line_term)
      {
      case 's':
      case 'p':if(cq2.Change_envir_EOL())
		   {
		   if (cq2.Lines[cq2.next].attr.Opened_Depth>0) /* Attributes are opened */
		     {
		     Close_All_Attr(cq2.Lines[cq2.next].attr,s); /* close attributes on EOL */
		     if((cq2.Lines[cq2.pre].Lstring.isEmpty()) && cq2.Lines[cq2.cur].Empty)
			s = replacesubstring(s,"\\/","");  //remove \/ in vertical mode
		     fputs(s(),cq2.LaTeX);
		     }
		   putc('\n', cq2.LaTeX);
		   cq2.CloseEnvironment();
		   }
	       else putc('\n', cq2.LaTeX);
	       break;

      case 'h': if(toupper(cq2.Lines[cq2.cur].envir) == 'I')
		  {                  
		  if (cq2.Lines[cq2.next].attr.Opened_Depth>0) /* Attributes are opened */
		     {
		     Close_All_Attr(cq2.Lines[cq2.next].attr,cq2.LaTeX); /* close attributes on EOL */
		     }
		  putc('\n', cq2.LaTeX);
		  cq2.CloseEnvironment();
		  cq2.Lines[cq2.cur].envir = ' ';
		  }

	       else {
		    cq2.underline = false;
		    for (i = 0; i < cq2.Lines[cq2.next].attr.Closed_Depth; i++)
		      cq2.underline = (cq2.underline ||
				       cq2.Lines[cq2.next].attr.stack[i] == 0xb || //DblUnd
				       cq2.Lines[cq2.next].attr.stack[i] == 0xd || //StrikeOut
				       cq2.Lines[cq2.next].attr.stack[i] == 0xe);  //Underline

		    if (cq2.underline && (cq2.Lines[cq2.next].attr.Opened_Depth>0) &&
			  !cq2.Lines[cq2.cur].attr.ClosedAttr() )
		      {
		      Close_All_Attr(cq2.Lines[cq2.next].attr,cq2.LaTeX); /* close attributes on EOL */
		      }

	/* Each Indent environment must be after one hard return Being secluded.*/

		   if(cq2.Change_envir_EOL())
			{
			if(cq2.just_envir_closed) fprintf(cq2.LaTeX, "\\nwln\n");
					      else putc('\n', cq2.LaTeX);
			if(cq2.Lines[cq2.next].attr.Opened_Depth>0) /* Attributes are opened */
				 {
				 Close_All_Attr(cq2.Lines[cq2.next].attr,cq2.LaTeX); /* close attributes on EOL */
				 }
			cq2.CloseEnvironment();
			}
		    else {
			 if(cq2.Lines[cq2.next].envir=='^'); // do not solve anything at the end of the minipage
			 else if(aLineStr->isEmpty() &&
				cq2.Lines[cq2.pre].Lstring.isEmpty())
				   {
				   fprintf(cq2.LaTeX, "\\bigskip");
				   }
			 else if(cq2.Lines[cq2.next].Empty>0)
				{
				cq2.Lines[cq2.next].InhibitBsBslash = true;
				}
			 else if(!cq2.Lines[cq2.cur].InhibitBsBslash)
				{
/*				if (!cq2.just_envir_closed && (cq2.Lines[cq2.cur].envir=='L' || cq2.Lines[cq2.cur].envir=='R'))
					 fprintf(cq2.LaTeX, "\\nwln");
				   else*/ {
					if(cq2.Lines[cq2.cur].Empty>0)
						 {
						 if(!cq2.Lines[cq2.pre].Lstring.isEmpty())
							fputc('\n',cq2.LaTeX);
						 fprintf(cq2.LaTeX, " \\bigskip");
						 }
					    else fprintf(cq2.LaTeX, "\\\\");
					}

				if (cq2.Lines[cq2.next].Lstring[0] == '[')
					  {
					  FixBracket(cq2.Lines[cq2.next].Lstring);
					  }
				}
		   putc('\n', cq2.LaTeX);
		   }
		 }
	     break;

    case 'P':if(cq2.Lines[cq2.next].attr.Opened_Depth>0) /* Attributes are opened */
		     {
		     Close_All_Attr(cq2.Lines[cq2.next].attr,s); /* close attributes on EOL */
		     if((cq2.Lines[cq2.pre].Lstring.isEmpty()) && cq2.Lines[cq2.cur].Empty)
			s = replacesubstring(s,"\\/","");  //remove \/ in vertical mode
		     fputs(s(),cq2.LaTeX);
		     }
	     putc('\n', cq2.LaTeX);
	     cq2.CloseEnvironment();
	     if(cq2.Lines[cq2.cur].Columns<=1 || Columns<3 || cq2.Col2Break>=cq2.Lines[cq2.cur].Columns)
	       {
	       fprintf(cq2.LaTeX, "\\newpage\n");
	       cq2.Col2Break=1;
	       }
	     else
	       {
	       fprintf(cq2.LaTeX, "\\columnbreak\n");	// every columnth's break do rather newpage
	       cq2.Col2Break++;
	       }
	     cq2.Lines[cq2.cur].envir = ' ';
	     break;
      }

EmptyThisLine:
    cq2.Lines[cq2.pre].Lstring.erase();
    cq2.Lines[cq2.pre].InhibitBsBslash = false;

    cq2.Select_NextLine();

			// read next row from strip file.
    fGets2(Strip, cq2.Lines[cq2.next2].Lstring);
    cq2.Lines[cq2.next2].Empty = EmptyLine(cq2.Lines[cq2.next2].Lstring);
    cq2.Lines[cq2.next2].Columns = cq2.Lines[cq2.next].Columns;			// propagate column flag.
    cq2.ReadTableEntry(cq2.next2);

#ifdef DEBUG_STOP_TEXT    
    if(StrStr(cq2.Lines[cq2.next2].Lstring, DEBUG_STOP_TEXT)!=NULL) debug_stop = true;
    DEBUG_CHECK
#endif


SkipThisLine:
    cq2.rownum++;
    }

  if(cq2.Lines[cq2.cur].envir!=' ' && cq2.Lines[cq2.pre].envir=='^')
		cq2.just_envir_closed = false;  //mark popped enviroment as open
  cq2.CloseEnvironment();	//Close enviroment if still opened
  if(Columns>=3 && cq2.Lines[cq2.pre].Columns>1) fprintf(cq2.LaTeX,"\\end{multicols}\n");
  cq2.Latex_foot();

  if(Verbosing >= 1)		//finishing a procentage counter
     {
     cq2.perc.SetPercent(100);
     }
}

/* End of pass2.cc */
