]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/tex2lyx.C
lots of small stuff
[lyx.git] / src / tex2lyx / tex2lyx.C
1 /** The .tex to .lyx converter
2     \author André Pönitz (2003)
3  */
4
5 // {[(
6
7 #include <config.h>
8
9 #include <algorithm>
10 #include <cctype>
11 #include <fstream>
12 #include <iostream>
13 #include <map>
14 #include <stack>
15 #include <string>
16 #include <vector>
17
18 #include "Lsstream.h"
19
20 #include "texparser.h"
21
22 using std::count_if;
23 using std::cout;
24 using std::cerr;
25 using std::endl;
26 using std::fill;
27 using std::getline;
28 using std::ios;
29 using std::ifstream;
30 using std::istream;
31 using std::istringstream;
32 using std::map;
33 using std::swap;
34 using std::ostream;
35 using std::ostringstream;
36 using std::stack;
37 using std::string;
38 using std::vector;
39
40
41 namespace {
42
43 struct ColInfo
44 {
45         ColInfo() : rightline(false) {}
46         string align;      // column alignment
47         string width;      // column width
48         bool   rightline;  // a line on the right?
49 };
50
51
52 struct RowInfo
53 {
54         RowInfo() : topline(false), bottomline(false) {} 
55         bool topline;     // horizontal line above
56         bool bottomline;  // horizontal line below
57 };
58
59
60 struct CellInfo
61 {
62         CellInfo()
63                 : multi(0), leftline(false), rightline(false),
64            topline(false), bottomline(false)
65         {}
66
67         string content;    // cell content
68         int multi;         // multicolumn flag
69         string align;      // cell alignment
70         bool leftline;     // do we have a line on the left?
71         bool rightline;    // do we have a line on the right?
72         bool topline;        // do we have a line above?
73         bool bottomline;   // do we have a line below?
74 };
75
76
77 void parse_preamble(Parser & p, ostream & os);
78
79 void parse(Parser & p, ostream & os, unsigned flags, const mode_type mode,
80 const bool outer);
81
82 string parse(Parser & p, unsigned flags, const mode_type mode,
83 const bool outer)
84 {
85         ostringstream os;
86         parse(p, os, flags, mode, outer);
87         return os.str();
88 }
89
90 int string2int(string const & s, int deflt = 0)
91 {
92         istringstream is(s);
93         int i = deflt;
94         is >> i;
95         return i;
96 }
97
98 string read_hlines(Parser & p)
99 {
100         ostringstream os;
101         p.skipSpaces();
102         while (p.good()) {
103                 if (p.nextToken().cs() == "hline") {
104                         p.getToken();
105                         os << "\\hline";
106                 } else if (p.nextToken().cs() == "cline") {
107                         p.getToken();
108                         os << "\\cline{" << p.verbatimItem() << "}";
109                 } else
110                         break;
111                 p.skipSpaces();
112         };
113         //cerr << "read_hlines(), read: '" << os.str() << "'\n";
114         //cerr << "read_hlines(), next token: " << p.nextToken() << "\n";
115         return os.str();
116 }
117
118
119
120 /* rather brutish way to code table structure in a string:
121
122   \begin{tabular}{ccc}
123     1 & 2 & 3\\ \hline
124     \multicolumn{2}{c}{4} & 5 //
125     6 & 7 \\
126   \end{tabular}
127
128  gets "translated" to:
129
130   1 TAB 2 TAB 3 LINE
131   \hline HLINE  TAB 5 LINE
132   5 TAB 7 LINE
133 */
134
135 char const TAB   = '\001';
136 char const LINE  = '\002';
137 char const HLINE = '\004';
138
139 const char * known_languages[] = { "austrian", "babel", "bahasa", "basque",
140 "breton", "british", "bulgarian", "catalan", "croatian", "czech", "danish",
141 "dutch", "english", "esperanto", "estonian", "finnish", "francais",
142 "frenchb", "galician", "german", "germanb", "greek", "hebcal", "hebfont",
143 "hebrew", "hebrew_newcode", "hebrew_oldcode", "hebrew_p", "hyphen",
144 "icelandic", "irish", "italian", "latin", "lgrcmr", "lgrcmro", "lgrcmss",
145 "lgrcmtt", "lgrenc", "lgrlcmss", "lgrlcmtt", "lheclas", "lhecmr",
146 "lhecmss", "lhecmtt", "lhecrml", "lheenc", "lhefr", "lheredis", "lheshold",
147 "lheshscr", "lheshstk", "lsorbian", "magyar", "naustrian", "ngermanb",
148 "ngerman", "norsk", "polish", "portuges", "rlbabel", "romanian",
149 "russianb", "samin", "scottish", "serbian", "slovak", "slovene", "spanish",
150 "swedish", "turkish", "ukraineb", "usorbian", "welsh", 0};
151
152 char const * known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
153
154 char const * known_headings[] = { "caption", "title", "author", "date",
155 "paragraph", "chapter", "section", "subsection", "subsubsection", 0 };
156
157 char const * known_math_envs[] = { "equation", "equation*",
158 "eqnarray", "eqnarray*", "align", "align*", 0};
159
160 char const * known_latex_commands[] = { "ref", "cite", "label", "index",
161 "printindex", "pageref", "url", 0 };
162
163 // LaTeX names for quotes
164 char const * known_quotes[] = { "glqq", "grqq", "quotedblbase",
165 "textquotedblleft", 0};
166
167 // the same as known_quotes with .lyx names
168 char const * known_coded_quotes[] = { "gld", "grd", "gld", "grd", 0};
169
170
171
172 // some ugly stuff
173 ostringstream h_preamble;
174 string h_textclass               = "article";
175 string h_options                 = "";
176 string h_language                = "english";
177 string h_inputencoding           = "latin1";
178 string h_fontscheme              = "default";
179 string h_graphics                = "default";
180 string h_paperfontsize           = "default";
181 string h_spacing                 = "single";
182 string h_papersize               = "default";
183 string h_paperpackage            = "default";
184 string h_use_geometry            = "0";
185 string h_use_amsmath             = "0";
186 string h_use_natbib              = "0";
187 string h_use_numerical_citations = "0";
188 string h_paperorientation        = "portrait";
189 string h_secnumdepth             = "3";
190 string h_tocdepth                = "3";
191 string h_paragraph_separation    = "indent";
192 string h_defskip                 = "medskip";
193 string h_quotes_language         = "english";
194 string h_quotes_times            = "2";
195 string h_papercolumns            = "1";
196 string h_papersides              = "1";
197 string h_paperpagestyle          = "default";
198 string h_tracking_changes        = "0";
199
200 // current stack of nested environments
201 stack<string> active_environments;
202
203
204 string cap(string s)
205 {
206         if (s.size())
207                 s[0] = toupper(s[0]);
208         return s;
209 }
210
211
212 string const trim(string const & a, char const * p = " \t\n\r")
213 {
214         // lyx::Assert(p);
215
216         if (a.empty() || !*p)
217                 return a;
218
219         string::size_type r = a.find_last_not_of(p);
220         string::size_type l = a.find_first_not_of(p);
221
222         // Is this the minimal test? (lgb)
223         if (r == string::npos && l == string::npos)
224                 return string();
225
226         return a.substr(l, r - l + 1);
227 }
228
229
230 void split(string const & s, vector<string> & result, char delim = ',')
231 {
232         //cerr << "split 1: '" << s << "'\n";
233         istringstream is(s);
234         string t;
235         while (getline(is, t, delim))
236                 result.push_back(t);
237         //cerr << "split 2\n";
238 }
239
240
241 // splits "x=z, y=b" into a map
242 map<string, string> split_map(string const & s)
243 {
244         map<string, string> res;
245         vector<string> v;
246         split(s, v);
247         for (size_t i = 0; i < v.size(); ++i) {
248                 size_t const pos   = v[i].find('=');
249                 string const index = v[i].substr(0, pos);
250                 string const value = v[i].substr(pos + 1, string::npos);
251                 res[trim(index)] = trim(value);
252         }
253         return res;
254 }
255
256
257 string join(vector<string> const & input, char const * delim)
258 {
259         ostringstream os;
260         for (size_t i = 0; i < input.size(); ++i) {
261                 if (i)
262                         os << delim;
263                 os << input[i];
264         }
265         return os.str();
266 }
267
268
269 char const ** is_known(string const & str, char const ** what)
270 {
271         for ( ; *what; ++what)
272                 if (str == *what)
273                         return what;
274         return 0;
275 }
276
277
278 void handle_opt(vector<string> & opts, char const ** what, string & target)
279 {
280         if (opts.empty())
281                 return;
282
283         for ( ; *what; ++what) {
284                 vector<string>::iterator it = find(opts.begin(), opts.end(), *what);
285                 if (it != opts.end()) {
286                         //cerr << "### found option '" << *what << "'\n";
287                         target = *what;
288                         opts.erase(it);
289                         return;
290                 }
291         }
292 }
293
294
295 bool is_math_env(string const & name)
296 {
297         for (char const ** what = known_math_envs; *what; ++what)
298                 if (*what == name)
299                         return true;
300         return false;
301 }
302
303
304 void begin_inset(ostream & os, string const & name)
305 {
306         os << "\n\\begin_inset " << name;
307 }
308
309
310 void end_inset(ostream & os)
311 {
312         os << "\n\\end_inset\n\n";
313 }
314
315
316 string curr_env()
317 {
318         return active_environments.empty() ? string() : active_environments.top();
319 }
320
321
322 void handle_ert(ostream & os, string const & s)
323 {
324         begin_inset(os, "ERT");
325         os << "\nstatus Collapsed\n\n\\layout Standard\n\n";
326         for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
327                 if (*it == '\\')
328                         os << "\n\\backslash\n";
329                 else
330                         os << *it;
331         }
332         end_inset(os);
333 }
334
335
336 void handle_par(ostream & os)
337 {
338         if (active_environments.empty())
339                 return;
340         os << "\n\\layout ";
341         string s = curr_env();
342         if (s == "document" || s == "table")
343                 os << "Standard\n\n";
344         else if (s == "lyxcode")
345                 os << "LyX-Code\n\n";
346         else if (s == "lyxlist")
347                 os << "List\n\n";
348         else if (s == "thebibliography")
349                 os << "Bibliography\n\n";
350         else
351                 os << cap(s) << "\n\n";
352 }
353
354
355 void handle_package(string const & name, string const & options)
356 {
357         //cerr << "handle_package: '" << name << "'\n";
358         if (name == "a4wide") {
359                 h_papersize = "a4paper";
360                 h_paperpackage = "widemarginsa4";
361         } else if (name == "ae")
362                 h_fontscheme = "ae";
363         else if (name == "aecompl")
364                 h_fontscheme = "ae";
365         else if (name == "amsmath")
366                 h_use_amsmath = "1";
367         else if (name == "amssymb")
368                 h_use_amsmath = "1";
369         else if (name == "babel")
370                 ; // ignore this
371         else if (name == "fontenc")
372                 ; // ignore this
373         else if (name == "inputenc")
374                 h_inputencoding = options;
375         else if (name == "makeidx")
376                 ; // ignore this
377         else if (name == "verbatim")
378                 ; // ignore this
379         else if (is_known(name, known_languages)) {
380                 h_language = name;
381                 h_quotes_language = name;
382         } else {
383                 if (options.size())
384                         h_preamble << "\\usepackage[" << options << "]{" << name << "}\n";
385                 else
386                         h_preamble << "\\usepackage{" << name << "}\n";
387         }
388 }
389
390
391 bool handle_colalign(Parser & p, vector<ColInfo> & colinfo)
392 {
393         if (p.getToken().cat() != catBegin)
394                 cerr << "wrong syntax for table column alignment. '{' expected\n";
395
396         string nextalign = "block";
397         bool leftline = false;
398         for (Token t = p.getToken(); p.good() && t.cat() != catEnd; t = p.getToken()){
399 #ifdef FILEDEBUG
400                 cerr << "t: " << t << "  c: '" << t.character() << "'\n";
401 #endif
402
403                 switch (t.character()) {
404                         case 'c':
405                                 colinfo.push_back(ColInfo());
406                                 colinfo.back().align = "center";
407                                 break;
408                         case 'l':
409                                 colinfo.push_back(ColInfo());
410                                 colinfo.back().align = "left";
411                                 break;
412                         case 'r':
413                                 colinfo.push_back(ColInfo());
414                                 colinfo.back().align = "right";
415                                 break;
416                         case 'p':
417                                 colinfo.push_back(ColInfo());
418                                 colinfo.back().align = nextalign;
419                                 colinfo.back().width = p.verbatimItem();
420                                 nextalign = "block";
421                                 break;
422                         case '|':
423                                 if (colinfo.empty())
424                                         leftline = true;
425                                 else
426                                         colinfo.back().rightline = true;
427                                 break;
428                         case '>': {
429                                 string s = p.verbatimItem();
430                                 if (s == "\\raggedleft ")
431                                         nextalign = "left";
432                                 else if (s == "\\raggedright ")
433                                         nextalign = "right";
434                                 else
435                                         cerr << "unknown '>' column '" << s << "'\n";
436                                 break;
437                         }
438                         default:
439                                 cerr << "ignoring special separator '" << t << "'\n";
440                                 break;
441                         }
442         }
443         return leftline;
444 }
445
446
447
448 void handle_tabular(Parser & p, ostream & os, mode_type mode)
449 {
450         begin_inset(os, "Tabular \n");
451         string posopts = p.getOpt();
452         if (posopts.size())
453                 cerr << "vertical tabular positioning '" << posopts << "' ignored\n";
454
455         vector<ColInfo>            colinfo;
456
457         // handle column formatting
458         bool leftline = handle_colalign(p, colinfo);
459
460         // handle initial hlines
461
462         // first scan of cells
463         // use table mode to keep it minimal-invasive
464         // not exactly what's TeX doing...
465         vector<string> lines;
466         ostringstream ss;
467         ss << read_hlines(p) << HLINE; // handle initial hlines
468         parse(p, ss, FLAG_END, TABLE_MODE, false);
469         split(ss.str(), lines, LINE);
470
471         vector< vector<CellInfo> > cellinfo(lines.size());
472         vector<RowInfo> rowinfo(lines.size());
473         
474         // split into rows
475         //cerr << "// split into rows\n";
476         for (size_t row = 0; row < rowinfo.size(); ++row) {
477
478                 // init row
479                 vector<CellInfo> & cellinfos = cellinfo[row];
480                 cellinfos.resize(colinfo.size());
481
482                 // split row    
483                 vector<string> dummy;
484                 //cerr << "\n########### LINE: " << lines[row] << "########\n";
485                 split(lines[row], dummy, HLINE);
486
487                 // handle horizontal line fragments
488                 if (dummy.size() != 3) {
489                         if (dummy.size() != 1)
490                                 cerr << "unexpected dummy size: " << dummy.size()
491                                         << " content: " << lines[row] << "\n";
492                         dummy.resize(3);
493                 }
494                 lines.at(row) = dummy.at(1);
495
496                 //cerr << "line: " << row << " above 0: " << dummy[0] << "\n";
497                 //cerr << "line: " << row << " below 2: " << dummy[2] <<  "\n";
498                 //cerr << "line: " << row << " cells 1: " << dummy[1] <<  "\n";
499
500                 for (int i = 0; i <= 2; i += 2) {       
501                         //cerr << "   reading from line string '" << dummy[i] << "'\n";
502                         Parser p1(dummy.at(i));
503                         while (p1.good()) {
504                                 Token t = p1.getToken();
505                                 //cerr << "read token: " << t << "\n";
506                                 if (t.cs() == "hline") {
507                                         if (i == 0) {
508                                                 rowinfo[row].topline = true;
509                                                 for (size_t c = 0; c < colinfo.size(); ++c)
510                                                         cellinfos.at(c).topline = true;
511                                         } else {
512                                                 rowinfo[row].bottomline = true;
513                                                 for (size_t c = 0; c < colinfo.size(); ++c)
514                                                         cellinfos.at(c).bottomline = true;
515                                         }
516                                 } else if (t.cs() == "cline") {
517                                         string arg = p1.verbatimItem();
518                                         //cerr << "read cline arg: '" << arg << "'\n";
519                                         vector<string> t;
520                                         split(arg, t, '-');
521                                         t.resize(2);
522                                         size_t from = string2int(t.at(0));
523                                         size_t to = string2int(t.at(1));
524                                         for (size_t col = from; col < to; ++col) {
525                                                 if (i == 0) 
526                                                         cellinfos.at(col).topline = true;
527                                                 else    
528                                                         cellinfos.at(col).bottomline = true;
529                                         }
530                                 } else {
531                                         cerr << "unexpected line token: " << t << endl;
532                                 }
533                         }
534                 }
535
536                 // split into cells
537                 vector<string> cells;
538                 split(lines[row], cells, TAB);
539                 for (size_t col = 0, cell = 0; cell < cells.size() && col < colinfo.size(); ++col, ++cell) {
540                         //cerr << "cell content: " << cells.at(cell) << "\n";
541                         Parser p(cells.at(cell));
542                         p.skipSpaces(); 
543                         //cerr << "handling cell: " << p.nextToken().cs() << " '" <<
544                         //cells.at(cell) << "'\n";
545                         if (p.nextToken().cs() == "multicolumn") {
546                                 // how many cells?
547                                 p.getToken();
548                                 size_t ncells = string2int(p.verbatimItem());
549
550                                 // special cell properties alignment    
551                                 vector<ColInfo> t;
552                                 bool leftline = handle_colalign(p, t);
553                                 CellInfo & ci = cellinfos.at(col);
554                                 ci.multi     = 1;
555                                 ci.align     = t.front().align;
556                                 ci.content   = parse(p, FLAG_ITEM, mode, false);
557                                 ci.leftline  = leftline;
558                                 ci.rightline = t.front().rightline;
559
560                                 // add dummy cells for multicol
561                                 for (size_t i = 0; i < ncells - 1 && col < colinfo.size(); ++i) {
562                                         ++col;
563                                         cellinfos.at(col).multi = 2;
564                                         cellinfos.at(col).align = "center";
565                                 }
566                         } else {
567                                 cellinfos.at(col).content = parse(p, FLAG_ITEM, mode, false);
568                         }
569                 }
570
571                 cellinfo.push_back(cellinfos);
572
573                 //cerr << "//  handle almost empty last row what we have\n";
574                 // handle almost empty last row
575                 if (row && lines.at(row).empty() && row + 1 == rowinfo.size()) {
576                         //cerr << "remove empty last line\n";
577                         if (rowinfo.at(row).topline);
578                                 rowinfo.at(row - 1).bottomline = true;
579                         for (size_t c = 0; c < colinfo.size(); ++c)
580                                 if (cellinfo.at(row).at(c).topline)
581                                         cellinfo.at(row - 1).at(c).bottomline = true;
582                         rowinfo.pop_back();
583                 }
584
585         }
586
587         //cerr << "// output what we have\n";
588         // output what we have
589         os << "<lyxtabular version=\"3\" rows=\"" << rowinfo.size()
590                  << "\" columns=\"" << colinfo.size() << "\">\n"
591                  << "<features>\n";
592
593         //cerr << "// after header\n";
594         for (size_t col = 0; col < colinfo.size(); ++col) {
595                 os << "<column alignment=\"" << colinfo.at(col).align << "\"";
596                 if (colinfo.at(col).rightline)
597                         os << " rightline=\"true\"";
598                 if (col == 0 && leftline)
599                         os << " leftline=\"true\"";
600                 os << " valignment=\"top\"";
601                 os << " width=\"" << colinfo.at(col).width << "\"";
602                 os << ">\n";
603         }
604         //cerr << "// after cols\n";
605
606         for (size_t row = 0; row < rowinfo.size(); ++row) {
607                 os << "<row";
608                 if (rowinfo[row].topline)
609                         os << " topline=\"true\"";
610                 if (rowinfo[row].bottomline)
611                         os << " bottomline=\"true\"";
612                 os << ">\n";
613                 for (size_t col = 0; col < colinfo.size(); ++col) {
614                         CellInfo const & cell = cellinfo[row][col];
615                         os << "<cell";
616                         if (cell.multi)
617                                 os << " multicolumn=\"" << cell.multi << "\"";
618                         if (cell.leftline)
619                                 os << " leftline=\"true\"";
620                         if (cell.rightline)
621                                 os << " rightline=\"true\"";
622                         if (cell.topline)
623                                 os << " topline=\"true\"";
624                         if (cell.bottomline)
625                                 os << " bottomline=\"true\"";
626                         os << " alignment=\"" << cell.align << "\""
627                                  << " valignment=\"top\""
628                                  << " usebox=\"none\""
629                                  << ">";
630                         begin_inset(os, "Text");
631                         os << "\n\n\\layout Standard\n\n";
632                         os << cell.content;
633                         end_inset(os);
634                         os << "</cell>\n";
635                 }
636                 os << "</row>\n";
637         }
638                         
639         os << "</lyxtabular>\n";
640         end_inset(os);
641 }
642
643
644 void end_preamble(ostream & os)
645 {
646         os << "# tex2lyx 0.0.2 created this file\n"
647            << "\\lyxformat 222\n"
648            << "\\textclass " << h_textclass << "\n"
649            << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n"
650            << "\\options " << h_options << "\n"
651            << "\\language " << h_language << "\n"
652            << "\\inputencoding " << h_inputencoding << "\n"
653            << "\\fontscheme " << h_fontscheme << "\n"
654            << "\\graphics " << h_graphics << "\n"
655            << "\\paperfontsize " << h_paperfontsize << "\n"
656            << "\\spacing " << h_spacing << "\n"
657            << "\\papersize " << h_papersize << "\n"
658            << "\\paperpackage " << h_paperpackage << "\n"
659            << "\\use_geometry " << h_use_geometry << "\n"
660            << "\\use_amsmath " << h_use_amsmath << "\n"
661            << "\\use_natbib " << h_use_natbib << "\n"
662            << "\\use_numerical_citations " << h_use_numerical_citations << "\n"
663            << "\\paperorientation " << h_paperorientation << "\n"
664            << "\\secnumdepth " << h_secnumdepth << "\n"
665            << "\\tocdepth " << h_tocdepth << "\n"
666            << "\\paragraph_separation " << h_paragraph_separation << "\n"
667            << "\\defskip " << h_defskip << "\n"
668            << "\\quotes_language " << h_quotes_language << "\n"
669            << "\\quotes_times " << h_quotes_times << "\n"
670            << "\\papercolumns " << h_papercolumns << "\n"
671            << "\\papersides " << h_papersides << "\n"
672            << "\\paperpagestyle " << h_paperpagestyle << "\n"
673            << "\\tracking_changes " << h_tracking_changes << "\n";
674 }
675
676
677 void parse_preamble(Parser & p, ostream & os)
678 {
679         while (p.good()) {
680                 Token const & t = p.getToken();
681
682 #ifdef FILEDEBUG
683                 cerr << "t: " << t << " flags: " << flags << "\n";
684                 //cell->dump();
685 #endif
686
687                 //
688                 // cat codes
689                 //
690                 if (t.cat() == catLetter ||
691                           t.cat() == catSpace ||
692                           t.cat() == catSuper ||
693                           t.cat() == catSub ||
694                           t.cat() == catOther ||
695                           t.cat() == catMath ||
696                           t.cat() == catActive ||
697                           t.cat() == catBegin ||
698                           t.cat() == catEnd ||
699                           t.cat() == catAlign ||
700                           t.cat() == catNewline ||
701                           t.cat() == catParameter)
702                 h_preamble << t.character();
703
704                 else if (t.cat() == catComment) {
705                         string s;
706                         while (p.good()) {
707                                 Token const & t = p.getToken();
708                                 if (t.cat() == catNewline)
709                                         break;
710                                 s += t.asString();
711                         }
712                         //cerr << "comment\n";
713                         p.skipSpaces();
714                 }
715
716                 else if (t.cs() == "pagestyle")
717                         h_paperpagestyle == p.verbatimItem();
718
719                 else if (t.cs() == "makeatletter") {
720                         p.setCatCode('@', catLetter);
721                         h_preamble << "\\makeatletter\n";
722                 }
723
724                 else if (t.cs() == "makeatother") {
725                         p.setCatCode('@', catOther);
726                         h_preamble << "\\makeatother\n";
727                 }
728
729                 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
730                             || t.cs() == "providecommand") {
731                         bool star = false;
732                         if (p.nextToken().character() == '*') {
733                                 p.getToken();
734                                 star = true;
735                         }
736                         string const name = p.verbatimItem();
737                         string const opts = p.getOpt();
738                         string const body = p.verbatimItem();
739                         // only non-lyxspecific stuff
740                         if (name != "\\noun "
741                                   && name != "\\tabularnewline "
742                             && name != "\\LyX "
743                                   && name != "\\lyxline "
744                                   && name != "\\lyxaddress "
745                                   && name != "\\lyxrightaddress "
746                                   && name != "\\boldsymbol "
747                                   && name != "\\lyxarrow ") {
748                                 ostringstream ss;
749                                 ss << '\\' << t.cs();
750                                 if (star)
751                                         ss << '*';
752                                 ss << '{' << name << '}' << opts << '{' << body << "}\n";
753                                 h_preamble << ss.str();
754 /*
755                                 ostream & out = in_preamble ? h_preamble : os;
756                                 out << "\\" << t.cs() << "{" << name << "}"
757                                     << opts << "{" << body << "}\n";
758                                 if (!in_preamble)
759                                         end_inset(os);
760 */
761                         }
762                 }
763
764                 else if (t.cs() == "documentclass") {
765                         vector<string> opts;
766                         split(p.getArg('[', ']'), opts, ',');
767                         handle_opt(opts, known_languages, h_language);
768                         handle_opt(opts, known_fontsizes, h_paperfontsize);
769                         h_quotes_language = h_language;
770                         h_options = join(opts, ",");
771                         h_textclass = p.getArg('{', '}');
772                 }
773
774                 else if (t.cs() == "usepackage") {
775                         string const options = p.getArg('[', ']');
776                         string const name = p.getArg('{', '}');
777                         if (options.empty() && name.find(',')) {
778                                 vector<string> vecnames;
779                                 split(name, vecnames, ',');
780                                 vector<string>::const_iterator it  = vecnames.begin();
781                                 vector<string>::const_iterator end = vecnames.end();
782                                 for (; it != end; ++it)
783                                         handle_package(trim(*it), string());
784                         } else {
785                                 handle_package(name, options);
786                         }
787                 }
788
789                 else if (t.cs() == "newenvironment") {
790                         string const name = p.getArg('{', '}');
791                         ostringstream ss;
792                         ss << "\\newenvironment{" << name << "}";
793                         ss << p.getOpt();
794                         ss << p.getOpt();
795                         ss << '{' << p.verbatimItem() << '}';
796                         ss << '{' << p.verbatimItem() << '}';
797                         ss << '\n';
798                         if (name != "lyxcode" && name != "lyxlist"
799                                         && name != "lyxrightadress" && name != "lyxaddress")
800                                 h_preamble << ss.str();
801                 }
802
803                 else if (t.cs() == "def") {
804                         string name = p.getToken().cs();
805                         while (p.nextToken().cat() != catBegin)
806                                 name += p.getToken().asString();
807                         h_preamble << "\\def\\" << name << '{' << p.verbatimItem() << "}\n";
808                 }
809
810                 else if (t.cs() == "setcounter") {
811                         string const name = p.getArg('{', '}');
812                         string const content = p.getArg('{', '}');
813                         if (name == "secnumdepth")
814                                 h_secnumdepth = content;
815                         else if (name == "tocdepth")
816                                 h_tocdepth = content;
817                         else
818                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}\n";
819                 }
820
821                 else if (t.cs() == "setlength") {
822                         string const name = p.verbatimItem();
823                         string const content = p.verbatimItem();
824                         if (name == "parskip")
825                                 h_paragraph_separation = "skip";
826                         else if (name == "parindent")
827                                 h_paragraph_separation = "skip";
828                         else
829                                 h_preamble << "\\setlength{" + name + "}{" + content + "}\n";
830                 }
831
832                 else if (t.cs() == "par")
833                         h_preamble << '\n';
834
835                 else if (t.cs() == "begin") {
836                         string const name = p.getArg('{', '}');
837                         if (name == "document") {
838                                 end_preamble(os);
839                                 os << "\n\n\\layout Standard\n\n";
840                                 return;
841                         }
842                         h_preamble << "\\begin{" << name << "}";
843                 }
844
845                 else if (t.cs().size())
846                         h_preamble << '\\' << t.cs() << ' ';
847         }
848 }
849
850
851 void parse(Parser & p, ostream & os, unsigned flags, const mode_type mode,
852 bool outer)
853 {
854         string hlines;
855
856         while (p.good()) {
857                 Token const & t = p.getToken();
858
859 #ifdef FILEDEBUG
860                 cerr << "t: " << t << " flags: " << flags << "\n";
861 #endif
862
863                 if (flags & FLAG_ITEM) {
864                         if (t.cat() == catSpace)
865                                 continue;
866
867                         flags &= ~FLAG_ITEM;
868                         if (t.cat() == catBegin) {
869                                 // skip the brace and collect everything to the next matching
870                                 // closing brace
871                                 flags |= FLAG_BRACE_LAST;
872                                 continue;
873                         }
874
875                         // handle only this single token, leave the loop if done
876                         flags |= FLAG_LEAVE;
877                 }
878
879
880                 if (flags & FLAG_BRACED) {
881                         if (t.cat() == catSpace)
882                                 continue;
883
884                         if (t.cat() != catBegin) {
885                                 p.error("opening brace expected");
886                                 return;
887                         }
888
889                         // skip the brace and collect everything to the next matching
890                         // closing brace
891                         flags = FLAG_BRACE_LAST;
892                 }
893
894
895                 if (flags & FLAG_OPTION) {
896                         if (t.cat() == catOther && t.character() == '[') {
897                                 parse(p, os, FLAG_BRACK_LAST, mode, outer);
898                         } else {
899                                 // no option found, put back token and we are done
900                                 p.putback();
901                         }
902                         return;
903                 }
904
905                 //
906                 // cat codes
907                 //
908                 if (t.cat() == catMath) {
909                         if (mode == TEXT_MODE || mode == MATHTEXT_MODE) {
910                                 // we are inside some text mode thingy, so opening new math is allowed
911                                 if (mode == TEXT_MODE)
912                                         begin_inset(os, "Formula ");
913                                 Token const & n = p.getToken();
914                                 if (n.cat() == catMath && outer) {
915                                         // TeX's $$...$$ syntax for displayed math
916                                         os << "\\[";
917                                         parse(p, os, FLAG_SIMPLE, MATH_MODE, outer);
918                                         os << "\\]";
919                                         p.getToken(); // skip the second '$' token
920                                 } else {
921                                         // simple $...$  stuff
922                                         p.putback();
923                                         os << '$';
924                                         parse(p, os, FLAG_SIMPLE, MATH_MODE, outer);
925                                         os << '$';
926                                 }
927                                 if (mode == TEXT_MODE)
928                                         end_inset(os);
929                         }
930
931                         else if (mode == TABLE_MODE) {
932                                 os << '$';
933                         }
934
935                         else if (flags & FLAG_SIMPLE) {
936                                 // this is the end of the formula
937                                 return;
938                         }
939
940                         else {
941                                 cerr << "\nmode: " << mode << endl;
942                                 p.error("something strange in the parser\n");
943                                 break;
944                         }
945                 }
946
947                 else if (t.cat() == catLetter ||
948                                t.cat() == catSpace ||
949                                t.cat() == catSuper ||
950                                t.cat() == catSub ||
951                                t.cat() == catOther ||
952                                t.cat() == catParameter)
953                         os << t.character();
954
955                 else if (t.cat() == catNewline) {
956                         if (p.nextToken().cat() == catNewline) {
957                                 p.getToken();
958                                 handle_par(os);
959                         } else {
960                                 os << "\n "; // note the space
961                         }
962                 }
963
964                 else if (t.cat() == catActive) {
965                         if (t.character() == '~') {
966                                 if (curr_env() == "lyxcode")
967                                         os << ' ';
968                                 else if (mode == TEXT_MODE)
969                                         os << "\\SpecialChar ~\n";
970                                 else
971                                         os << '~';
972                         } else
973                                 os << t.character();
974                 }
975
976                 else if (t.cat() == catBegin) {
977                         if (mode == TEXT_MODE) {
978                                 handle_ert(os, "{");
979                                 parse(p, os, FLAG_BRACE_LAST, mode, outer);
980                                 handle_ert(os, "}");
981                         } else {
982                                 os << '{';
983                         }
984                 }
985
986                 else if (t.cat() == catEnd) {
987                         if (flags & FLAG_BRACE_LAST)
988                                 return;
989                         if (mode == TEXT_MODE)
990                                 handle_ert(os, "}");
991                         else
992                                 os << '}';
993                 }
994
995                 else if (t.cat() == catAlign) {
996                         if (mode == TABLE_MODE)
997                                 os << TAB;
998                         else
999                                 os << t.character();
1000                 }
1001
1002                 else if (t.cs() == "tabularnewline") {
1003                         if (mode == TABLE_MODE) {
1004                                 // stuff before the line break
1005                                 // and look ahead for stuff after the line break
1006                                 os << HLINE << hlines << HLINE << LINE << read_hlines(p) << HLINE;
1007                                 hlines.clear();
1008                         } else {
1009                                 os << t.asInput();
1010                         }
1011                 }
1012
1013                 else if (t.cs() == "\\" && mode == MATH_MODE)
1014                         os << t.asInput();
1015
1016                 else if (t.cs() == "\\" && mode == TEXT_MODE && curr_env() == "tabular")
1017                         os << LINE;
1018
1019                 else if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) {
1020                         //cerr << "finished reading option\n";
1021                         return;
1022                 }
1023
1024                 else if (t.cat() == catOther)
1025                         os << string(1, t.character());
1026
1027                 else if (t.cat() == catComment) {
1028                         string s;
1029                         while (p.good()) {
1030                                 Token const & t = p.getToken();
1031                                 if (t.cat() == catNewline)
1032                                         break;
1033                                 s += t.asString();
1034                         }
1035                         //cerr << "comment\n";
1036                         p.skipSpaces();
1037                 }
1038
1039                 //
1040                 // control sequences
1041                 //
1042
1043                 else if (t.cs() == "ldots" && mode == MATH_MODE)
1044                         os << "\n\\SpecialChar \\ldots{}\n";
1045
1046                 else if (t.cs() == "lyxlock")
1047                         ; // ignored
1048
1049                 else if (t.cs() == "makeatletter") {
1050                         p.setCatCode('@', catLetter);
1051                         handle_ert(os, "\\makeatletter\n");
1052                 }
1053
1054                 else if (t.cs() == "makeatother") {
1055                         p.setCatCode('@', catOther);
1056                         handle_ert(os, "\\makeatother\n");
1057                 }
1058
1059                 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
1060                             || t.cs() == "providecommand") {
1061                         string const name = p.verbatimItem();
1062                         string const opts = p.getOpt();
1063                         string const body = p.verbatimItem();
1064                         // only non-lyxspecific stuff
1065                         if (name != "\\noun " && name != "\\tabularnewline ") {
1066                                 ostringstream ss;
1067                                 ss << '\\' << t.cs() << '{' << name << '}'
1068                                         << opts << '{' << body << "}\n";
1069                                 handle_ert(os, ss.str());
1070 /*
1071                                 ostream & out = in_preamble ? h_preamble : os;
1072                                 if (!in_preamble)
1073                                         begin_inset(os, "FormulaMacro\n");
1074                                 out << "\\" << t.cs() << "{" << name << "}"
1075                                     << opts << "{" << body << "}\n";
1076                                 if (!in_preamble)
1077                                         end_inset(os);
1078 */
1079                         }
1080                 }
1081
1082                 else if (t.cs() == "newtheorem") {
1083                         ostringstream ss;
1084                         ss << "\\newtheorem";
1085                         ss << '{' << p.verbatimItem() << '}';
1086                         ss << p.getOpt();
1087                         ss << '{' << p.verbatimItem() << '}';
1088                         ss << p.getOpt();
1089                         ss << '\n';
1090                         handle_ert(os, ss.str());
1091                 }
1092
1093                 else if (t.cs() == "(") {
1094                         begin_inset(os, "Formula");
1095                         os << " \\(";
1096                         parse(p, os, FLAG_SIMPLE2, MATH_MODE, outer);
1097                         os << "\\)";
1098                         end_inset(os);
1099                 }
1100
1101                 else if (t.cs() == "[" && mode == TEXT_MODE) {
1102                         begin_inset(os, "Formula");
1103                         os << " \\[";
1104                         parse(p, os, FLAG_EQUATION, MATH_MODE, outer);
1105                         os << "\\]";
1106                         end_inset(os);
1107                 }
1108
1109                 else if (t.cs() == "protect")
1110                         // ignore \\protect, will hopefully be re-added during output
1111                         ;
1112
1113                 else if (t.cs() == "begin") {
1114                         string const name = p.getArg('{', '}');
1115                         active_environments.push(name);
1116                         if (name == "abstract") {
1117                                 handle_par(os);
1118                                 parse(p, os, FLAG_END, mode, outer);
1119                         } else if (is_math_env(name)) {
1120                                 begin_inset(os, "Formula ");
1121                                 os << "\\begin{" << name << "}";
1122                                 parse(p, os, FLAG_END, MATH_MODE, outer);
1123                                 os << "\\end{" << name << "}";
1124                                 end_inset(os);
1125                         } else if (name == "tabular") {
1126                                 if (mode == TEXT_MODE)
1127                                         handle_tabular(p, os, mode);
1128                                 else {
1129                                         os << "\\begin{" << name << "}";
1130                                         parse(p, os, FLAG_END, MATHTEXT_MODE, outer);
1131                                         os << "\\end{" << name << "}";
1132                                 }
1133                         } else if (name == "table" || name == "figure") {
1134                                 string opts = p.getOpt();
1135                                 begin_inset(os, "Float " + name + "\n");
1136                                 if (opts.size())
1137                                         os << "placement " << opts << '\n';
1138                                 os << "wide false\n"
1139                                          << "collapsed false\n"
1140                                          << "\n"
1141                                          << "\\layout Standard\n";
1142                                 parse(p, os, FLAG_END, mode, outer);
1143                                 end_inset(os);
1144                         } else if (name == "lyxlist") {
1145                                 p.verbatimItem(); // swallow next arg
1146                                 parse(p, os, FLAG_END, mode, outer);
1147                                 os << "\n\\layout Bibliography\n\n";
1148                         } else if (name == "thebibliography") {
1149                                 p.verbatimItem(); // swallow next arg
1150                                 parse(p, os, FLAG_END, mode, outer);
1151                                 os << "\n\\layout Bibliography\n\n";
1152                         } else if (mode == MATH_MODE || mode == MATHTEXT_MODE) {
1153                                 os << "\\begin{" << name << "}";
1154                                 parse(p, os, FLAG_END, mode, outer);
1155                                 os << "\\end{" << name << "}";
1156                         } else {
1157                                 parse(p, os, FLAG_END, mode, outer);
1158                         }
1159                 }
1160
1161                 else if (t.cs() == "end") {
1162                         if (flags & FLAG_END) {
1163                                 // eat environment name
1164                                 string const name = p.getArg('{', '}');
1165                                 if (name != curr_env())
1166                                         p.error("\\end{" + name + "} does not match \\begin{"
1167                                                 + curr_env() + "}");
1168                                 active_environments.pop();
1169                                 return;
1170                         }
1171                         p.error("found 'end' unexpectedly");
1172                 }
1173
1174                 else if (t.cs() == "item")
1175                         handle_par(os);
1176
1177                 else if (t.cs() == ")") {
1178                         if (flags & FLAG_SIMPLE2)
1179                                 return;
1180                         p.error("found '\\)' unexpectedly");
1181                 }
1182
1183                 else if (t.cs() == "]") {
1184                         if (flags & FLAG_EQUATION)
1185                                 return;
1186                         p.error("found '\\]' unexpectedly");
1187                 }
1188
1189                 else if (t.cs() == "documentclass") {
1190                         vector<string> opts;
1191                         split(p.getArg('[', ']'), opts, ',');
1192                         handle_opt(opts, known_languages, h_language);
1193                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1194                         h_quotes_language = h_language;
1195                         h_options = join(opts, ",");
1196                         h_textclass = p.getArg('{', '}');
1197                 }
1198
1199                 else if (t.cs() == "usepackage") {
1200                         string const options = p.getArg('[', ']');
1201                         string const name = p.getArg('{', '}');
1202                         if (options.empty() && name.find(',')) {
1203                                 vector<string> vecnames;
1204                                 split(name, vecnames, ',');
1205                                 vector<string>::const_iterator it  = vecnames.begin();
1206                                 vector<string>::const_iterator end = vecnames.end();
1207                                 for (; it != end; ++it)
1208                                         handle_package(trim(*it), string());
1209                         } else {
1210                                 handle_package(name, options);
1211                         }
1212                 }
1213
1214                 else if (t.cs() == "def") {
1215                         string name = p.getToken().cs();
1216                         while (p.nextToken().cat() != catBegin)
1217                                 name += p.getToken().asString();
1218                         handle_ert(os, "\\def\\" + name + '{' + p.verbatimItem() + '}');
1219                 }
1220
1221                 else if (t.cs() == "par")
1222                         handle_par(os);
1223
1224                 else if (is_known(t.cs(), known_headings)) {
1225                         string name = t.cs();
1226                         if (p.nextToken().asInput() == "*") {
1227                                 p.getToken();
1228                                 name += "*";
1229                         }
1230                         os << "\n\n\\layout " << cap(name) << "\n\n";
1231                         string opt = p.getOpt();
1232                         if (opt.size()) {
1233                                 begin_inset(os, "OptArg\n");
1234                                 os << "collapsed true\n\n\\layout Standard\n\n" << opt;
1235                                 end_inset(os);
1236                         }
1237                         parse(p, os, FLAG_ITEM, mode, outer);
1238                         os << "\n\n\\layout Standard\n\n";
1239                 }
1240
1241                 else if (t.cs() == "includegraphics") {
1242                         if (mode == TEXT_MODE) {
1243                                 map<string, string> opts = split_map(p.getArg('[', ']'));
1244                                 string name = p.verbatimItem();
1245                                 begin_inset(os, "Graphics ");
1246                                 os << "\n\tfilename " << name << '\n';
1247                                 if (opts.find("width") != opts.end())
1248                                         os << "\twidth " << opts["width"] << '\n';
1249                                 if (opts.find("height") != opts.end())
1250                                         os << "\theight " << opts["height"] << '\n';
1251                                 end_inset(os);
1252                         } else {
1253                                 os << "\\includegraphics ";
1254                         }
1255                 }
1256                 
1257                 else if (t.cs() == "footnote") {
1258                         begin_inset(os, "Foot\n");
1259                         os << "collapsed true\n\n\\layout Standard\n\n";
1260                         parse(p, os, FLAG_ITEM, mode, false);
1261                         end_inset(os);
1262                 }
1263
1264                 else if (t.cs() == "makeindex" || t.cs() == "maketitle")
1265                         ; // swallow this
1266
1267                 else if (t.cs() == "tableofcontents")
1268                         p.verbatimItem(); // swallow this
1269
1270                 else if (t.cs() == "hline" && mode == TABLE_MODE)
1271                         hlines += "\\hline";
1272
1273                 else if (t.cs() == "cline" && mode == TABLE_MODE)
1274                         hlines += "\\cline{" + p.verbatimItem() + '}';
1275
1276                 else if (t.cs() == "tiny" && mode == TEXT_MODE)
1277                         os << "\n\\size tiny\n";
1278
1279                 else if (t.cs() == "scriptsize" && mode == TEXT_MODE)
1280                         os << "\n\\size scriptsize\n";
1281
1282                 else if (t.cs() == "Large" && mode == TEXT_MODE)
1283                         os << "\n\\size larger\n";
1284
1285                 else if (t.cs() == "textrm") {
1286                         if (mode == TEXT_MODE) {
1287                                 os << "\n\\family roman\n";
1288                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1289                                 os << "\n\\family default\n";
1290                         } else {
1291                                 os << '\\' << t.cs() << '{';
1292                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1293                                 os << '}';
1294                         }
1295                 }
1296
1297                 else if (t.cs() == "textsf") {
1298                         if (mode == TEXT_MODE) {
1299                                 os << "\n\\family sans\n";
1300                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1301                                 os << "\n\\family default\n";
1302                         } else {
1303                                 os << '\\' << t.cs() << '{';
1304                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1305                                 os << '}';
1306                         }
1307                 }
1308
1309                 else if (t.cs() == "texttt") {
1310                         if (mode == TEXT_MODE) {
1311                                 os << "\n\\family typewriter\n";
1312                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1313                                 os << "\n\\family default\n";
1314                         } else {
1315                                 os << '\\' << t.cs() << '{';
1316                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1317                                 os << '}';
1318                         }
1319                 }
1320
1321                 else if (t.cs() == "textsc") {
1322                         if (mode == TEXT_MODE) {
1323                                 os << "\n\\noun on\n";
1324                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1325                                 os << "\n\\noun default\n";
1326                         } else {
1327                                 os << '\\' << t.cs() << '{';
1328                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1329                                 os << '}';
1330                         }
1331                 }
1332
1333                 else if (t.cs() == "textbf") {
1334                         if (mode == TEXT_MODE) {
1335                                 os << "\n\\series bold\n";
1336                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1337                                 os << "\n\\series default\n";
1338                         } else {
1339                                 os << '\\' << t.cs() << '{';
1340                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1341                                 os << '}';
1342                         }
1343                 }
1344
1345                 else if (t.cs() == "underbar") {
1346                         if (mode == TEXT_MODE) {
1347                                 os << "\n\\bar under\n";
1348                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1349                                 os << "\n\\bar default\n";
1350                         } else {
1351                                 os << '\\' << t.cs() << '{';
1352                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1353                                 os << '}';
1354                         }
1355                 }
1356
1357                 else if ((t.cs() == "emph" || t.cs() == "noun") && mode == TEXT_MODE) {
1358                         os << "\n\\" << t.cs() << " on\n";
1359                         parse(p, os, FLAG_ITEM, mode, outer);
1360                         os << "\n\\" << t.cs() << " default\n";
1361                 }
1362
1363                 else if (t.cs() == "mbox" && mode != TEXT_MODE) {
1364                         os << "\n\\mbox{";
1365                         parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1366                         os << '}';
1367                 }
1368
1369                 else if (is_known(t.cs(), known_latex_commands) && mode == TEXT_MODE) {
1370                         begin_inset(os, "LatexCommand ");
1371                         os << '\\' << t.cs();
1372                         os << p.getOpt();
1373                         os << p.getOpt();
1374                         os << '{' << p.verbatimItem() << '}';
1375                         end_inset(os);
1376                 }
1377
1378                 else if (t.cs() == "bibitem") {
1379                         os << "\n\\layout Bibliography\n\\bibitem ";
1380                         os << p.getOpt();
1381                         os << '{' << p.verbatimItem() << '}' << "\n\n";
1382                 }
1383
1384                 else if (mode == TEXT_MODE && is_known(t.cs(), known_quotes)) {
1385                   char const ** where = is_known(t.cs(), known_quotes);
1386                         begin_inset(os, "Quotes ");
1387                         os << known_coded_quotes[where - known_quotes];
1388                         end_inset(os);
1389                 }
1390
1391                 else if (t.cs() == "LyX" && mode == TEXT_MODE) {
1392                         p.verbatimItem(); // eat {}
1393                         os << "LyX";
1394                 }
1395
1396                 else if (t.cs() == "TeX" && mode == TEXT_MODE) {
1397                         p.verbatimItem(); // eat {}
1398                         os << "TeX";
1399                 }
1400
1401                 else if (t.cs() == "LaTeX" && mode == TEXT_MODE) {
1402                         p.verbatimItem(); // eat {}
1403                         os << "LaTeX";
1404                 }
1405
1406                 else if (t.cs() == "LaTeXe" && mode == TEXT_MODE) {
1407                         p.verbatimItem(); // eat {}
1408                         os << "LaTeXe";
1409                 }
1410
1411                 else if (t.cs() == "lyxarrow" && mode == TEXT_MODE) {
1412                         p.verbatimItem();
1413                         os << "\\SpecialChar \\menuseparator\n";
1414                 }
1415
1416                 else if (t.cs() == "ldots" && mode == TEXT_MODE) {
1417                         p.verbatimItem();
1418                         os << "\\SpecialChar \\ldots{}\n";
1419                 }
1420
1421                 else if (t.cs() == "@" && mode == TEXT_MODE)
1422                         os << "\\SpecialChar \\@";
1423
1424                 else if (t.cs() == "textasciitilde" && mode == TEXT_MODE)
1425                         os << '~';
1426
1427                 else if (t.cs() == "_" && mode == TEXT_MODE)
1428                         os << '_';
1429
1430                 else if (t.cs() == "&" && mode == TEXT_MODE)
1431                         os << '&';
1432
1433                 else if (t.cs() == "#" && mode == TEXT_MODE)
1434                         os << "#";
1435
1436                 else if (t.cs() == "\"") {
1437                         string const name = p.verbatimItem();
1438                              if (name == "a") os << 'ä';
1439                         else if (name == "o") os << 'ö';
1440                         else if (name == "u") os << 'ü';
1441                         else if (name == "A") os << 'Ä';
1442                         else if (name == "O") os << 'Ö';
1443                         else if (name == "U") os << 'Ü';
1444                         else handle_ert(os, "\"{" + name + "}");
1445                 }
1446
1447                 else if (t.cs() == "ss")
1448                         os << "ß";
1449
1450                 else if (t.cs() == "input")
1451                         handle_ert(os, "\\input{" + p.verbatimItem() + "}\n");
1452
1453                 else if (t.cs() == "fancyhead") {
1454                         ostringstream ss;
1455                         ss << "\\fancyhead";
1456                         ss << p.getOpt();
1457                         ss << '{' << p.verbatimItem() << "}\n";
1458                         handle_ert(os, ss.str());
1459                 }
1460
1461                 else {
1462                         //cerr << "#: " << t << " mode: " << mode << endl;
1463                         if (mode == TEXT_MODE) {
1464                                 // heuristic: read up to next non-nested space
1465                                 /*
1466                                 string s = t.asInput();
1467                                 string z = p.verbatimItem();
1468                                 while (p.good() && z != " " && z.size()) {
1469                                         //cerr << "read: " << z << endl;
1470                                         s += z;
1471                                         z = p.verbatimItem();
1472                                 }
1473                                 cerr << "found ERT: " << s << endl;
1474                                 handle_ert(os, s + ' ');
1475                                 */
1476                                 handle_ert(os, t.asInput() + ' ');
1477                         } else {
1478                                 os << t.asInput();
1479                                 //cerr << "#: writing: '" << t.asInput() << "'\n";
1480                         }
1481                 }
1482
1483                 if (flags & FLAG_LEAVE) {
1484                         flags &= ~FLAG_LEAVE;
1485                         break;
1486                 }
1487         }
1488 }
1489
1490
1491
1492 } // anonymous namespace
1493
1494
1495 int main(int argc, char * argv[])
1496 {
1497         if (argc <= 1) {
1498                 cerr << "Usage: " << argv[0] << " <infile.tex>" << endl;
1499                 return 2;
1500         }
1501
1502         ifstream is(argv[1]);
1503         Parser p(is);
1504         parse_preamble(p, cout);
1505         active_environments.push("document");
1506         parse(p, cout, FLAG_END, TEXT_MODE, true);
1507         cout << "\n\\the_end";
1508
1509         return 0;
1510 }
1511
1512 // }])