]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/tex2lyx.C
make Kayvan's compiler happy
[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[row] = dummy[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[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 col = 0; col < colinfo.size(); ++col)
510                                                         cellinfos[col].topline = true;
511                                         } else {
512                                                 rowinfo[row].bottomline = true;
513                                                 for (size_t col = 0; col < colinfo.size(); ++col)
514                                                         cellinfos[col].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[0]);
523                                         size_t to = string2int(t[1]);
524                                         for (size_t col = from; col < to; ++col) {
525                                                 if (i == 0) 
526                                                         cellinfos[col].topline = true;
527                                                 else    
528                                                         cellinfos[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[cell] << "\n";
541                         Parser p(cells[cell]);
542                         p.skipSpaces(); 
543                         //cerr << "handling cell: " << p.nextToken().cs() << " '" <<
544                         //cells[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[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[col].multi = 2;
564                                         cellinfos[col].align = "center";
565                                 }
566                         } else {
567                                 cellinfos[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[row].empty() && row + 1 == rowinfo.size()) {
576                         //cerr << "remove empty last line\n";
577                         if (rowinfo[row].topline);
578                                 rowinfo[row - 1].bottomline = true;
579                         for (size_t col = 0; col < colinfo.size(); ++col)
580                                 if (cellinfo[row][col].topline)
581                                         cellinfo[row - 1][col].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[col].align << "\"";
596                 if (colinfo[col].rightline)
597                         os << " rightline=\"true\"";
598                 if (col == 0 && leftline)
599                         os << " leftline=\"true\"";
600                 os << " valignment=\"top\"";
601                 os << " width=\"" << colinfo[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                 //
881                 // cat codes
882                 //
883                 if (t.cat() == catMath) {
884                         if (mode == TEXT_MODE || mode == MATHTEXT_MODE) {
885                                 // we are inside some text mode thingy, so opening new math is allowed
886                                 if (mode == TEXT_MODE)
887                                         begin_inset(os, "Formula ");
888                                 Token const & n = p.getToken();
889                                 if (n.cat() == catMath && outer) {
890                                         // TeX's $$...$$ syntax for displayed math
891                                         os << "\\[";
892                                         parse(p, os, FLAG_SIMPLE, MATH_MODE, outer);
893                                         os << "\\]";
894                                         p.getToken(); // skip the second '$' token
895                                 } else {
896                                         // simple $...$  stuff
897                                         p.putback();
898                                         os << '$';
899                                         parse(p, os, FLAG_SIMPLE, MATH_MODE, outer);
900                                         os << '$';
901                                 }
902                                 if (mode == TEXT_MODE)
903                                         end_inset(os);
904                         }
905
906                         else if (mode == TABLE_MODE) {
907                                 os << '$';
908                         }
909
910                         else if (flags & FLAG_SIMPLE) {
911                                 // this is the end of the formula
912                                 return;
913                         }
914
915                         else {
916                                 cerr << "\nmode: " << mode << endl;
917                                 p.error("something strange in the parser\n");
918                                 break;
919                         }
920                 }
921
922                 else if (t.cat() == catLetter ||
923                                t.cat() == catSpace ||
924                                t.cat() == catSuper ||
925                                t.cat() == catSub ||
926                                t.cat() == catOther ||
927                                t.cat() == catParameter)
928                         os << t.character();
929
930                 else if (t.cat() == catNewline) {
931                         if (p.nextToken().cat() == catNewline) {
932                                 p.getToken();
933                                 handle_par(os);
934                         } else {
935                                 os << "\n "; // note the space
936                         }
937                 }
938
939                 else if (t.cat() == catActive) {
940                         if (t.character() == '~') {
941                                 if (curr_env() == "lyxcode")
942                                         os << ' ';
943                                 else if (mode == TEXT_MODE)
944                                         os << "\\SpecialChar ~\n";
945                                 else
946                                         os << '~';
947                         } else
948                                 os << t.character();
949                 }
950
951                 else if (t.cat() == catBegin) {
952                         if (mode == TEXT_MODE) {
953                                 handle_ert(os, "{");
954                                 parse(p, os, FLAG_BRACE_LAST, mode, outer);
955                                 handle_ert(os, "}");
956                         } else {
957                                 os << '{';
958                         }
959                 }
960
961                 else if (t.cat() == catEnd) {
962                         if (flags & FLAG_BRACE_LAST)
963                                 return;
964                         if (mode == TEXT_MODE)
965                                 handle_ert(os, "}");
966                         else
967                                 os << '}';
968                 }
969
970                 else if (t.cat() == catAlign) {
971                         if (mode == TABLE_MODE)
972                                 os << TAB;
973                         else
974                                 os << t.character();
975                 }
976
977                 else if (t.cs() == "tabularnewline") {
978                         if (mode == TABLE_MODE) {
979                                 // stuff before the line break
980                                 // and look ahead for stuff after the line break
981                                 os << HLINE << hlines << HLINE << LINE << read_hlines(p) << HLINE;
982                                 hlines.clear();
983                         } else {
984                                 os << t.asInput();
985                         }
986                 }
987
988                 else if (t.cs() == "\\" && mode == MATH_MODE)
989                         os << t.asInput();
990
991                 else if (t.cs() == "\\" && mode == TEXT_MODE && curr_env() == "tabular")
992                         os << LINE;
993
994                 else if (t.cat() == catOther)
995                         os << string(1, t.character());
996
997                 else if (t.cat() == catComment) {
998                         string s;
999                         while (p.good()) {
1000                                 Token const & t = p.getToken();
1001                                 if (t.cat() == catNewline)
1002                                         break;
1003                                 s += t.asString();
1004                         }
1005                         //cerr << "comment\n";
1006                         p.skipSpaces();
1007                 }
1008
1009                 //
1010                 // control sequences
1011                 //
1012
1013                 else if (t.cs() == "ldots" && mode == MATH_MODE)
1014                         os << "\n\\SpecialChar \\ldots{}\n";
1015
1016                 else if (t.cs() == "lyxlock")
1017                         ; // ignored
1018
1019                 else if (t.cs() == "makeatletter") {
1020                         p.setCatCode('@', catLetter);
1021                         handle_ert(os, "\\makeatletter\n");
1022                 }
1023
1024                 else if (t.cs() == "makeatother") {
1025                         p.setCatCode('@', catOther);
1026                         handle_ert(os, "\\makeatother\n");
1027                 }
1028
1029                 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
1030                             || t.cs() == "providecommand") {
1031                         string const name = p.verbatimItem();
1032                         string const opts = p.getOpt();
1033                         string const body = p.verbatimItem();
1034                         // only non-lyxspecific stuff
1035                         if (name != "\\noun " && name != "\\tabularnewline ") {
1036                                 ostringstream ss;
1037                                 ss << '\\' << t.cs() << '{' << name << '}'
1038                                         << opts << '{' << body << "}\n";
1039                                 handle_ert(os, ss.str());
1040 /*
1041                                 ostream & out = in_preamble ? h_preamble : os;
1042                                 if (!in_preamble)
1043                                         begin_inset(os, "FormulaMacro\n");
1044                                 out << "\\" << t.cs() << "{" << name << "}"
1045                                     << opts << "{" << body << "}\n";
1046                                 if (!in_preamble)
1047                                         end_inset(os);
1048 */
1049                         }
1050                 }
1051
1052                 else if (t.cs() == "newtheorem") {
1053                         ostringstream ss;
1054                         ss << "\\newtheorem";
1055                         ss << '{' << p.verbatimItem() << '}';
1056                         ss << p.getOpt();
1057                         ss << '{' << p.verbatimItem() << '}';
1058                         ss << p.getOpt();
1059                         ss << '\n';
1060                         handle_ert(os, ss.str());
1061                 }
1062
1063                 else if (t.cs() == "(") {
1064                         begin_inset(os, "Formula");
1065                         os << " \\(";
1066                         parse(p, os, FLAG_SIMPLE2, MATH_MODE, outer);
1067                         os << "\\)";
1068                         end_inset(os);
1069                 }
1070
1071                 else if (t.cs() == "[" && mode == TEXT_MODE) {
1072                         begin_inset(os, "Formula");
1073                         os << " \\[";
1074                         parse(p, os, FLAG_EQUATION, MATH_MODE, outer);
1075                         os << "\\]";
1076                         end_inset(os);
1077                 }
1078
1079                 else if (t.cs() == "protect")
1080                         // ignore \\protect, will hopefully be re-added during output
1081                         ;
1082
1083                 else if (t.cs() == "begin") {
1084                         string const name = p.getArg('{', '}');
1085                         active_environments.push(name);
1086                         if (name == "abstract") {
1087                                 handle_par(os);
1088                                 parse(p, os, FLAG_END, mode, outer);
1089                         } else if (is_math_env(name)) {
1090                                 begin_inset(os, "Formula ");
1091                                 os << "\\begin{" << name << "}";
1092                                 parse(p, os, FLAG_END, MATH_MODE, outer);
1093                                 os << "\\end{" << name << "}";
1094                                 end_inset(os);
1095                         } else if (name == "tabular") {
1096                                 if (mode == TEXT_MODE)
1097                                         handle_tabular(p, os, mode);
1098                                 else {
1099                                         os << "\\begin{" << name << "}";
1100                                         parse(p, os, FLAG_END, MATHTEXT_MODE, outer);
1101                                         os << "\\end{" << name << "}";
1102                                 }
1103                         } else if (name == "table" || name == "figure") {
1104                                 string opts = p.getOpt();
1105                                 begin_inset(os, "Float " + name + "\n");
1106                                 if (opts.size())
1107                                         os << "placement " << opts << '\n';
1108                                 os << "wide false\n"
1109                                          << "collapsed false\n"
1110                                          << "\n"
1111                                          << "\\layout Standard\n";
1112                                 parse(p, os, FLAG_END, mode, outer);
1113                                 end_inset(os);
1114                         } else if (name == "lyxlist") {
1115                                 p.verbatimItem(); // swallow next arg
1116                                 parse(p, os, FLAG_END, mode, outer);
1117                                 os << "\n\\layout Bibliography\n\n";
1118                         } else if (name == "thebibliography") {
1119                                 p.verbatimItem(); // swallow next arg
1120                                 parse(p, os, FLAG_END, mode, outer);
1121                                 os << "\n\\layout Bibliography\n\n";
1122                         } else if (mode == MATH_MODE || mode == MATHTEXT_MODE) {
1123                                 os << "\\begin{" << name << "}";
1124                                 parse(p, os, FLAG_END, mode, outer);
1125                                 os << "\\end{" << name << "}";
1126                         } else {
1127                                 parse(p, os, FLAG_END, mode, outer);
1128                         }
1129                 }
1130
1131                 else if (t.cs() == "end") {
1132                         if (flags & FLAG_END) {
1133                                 // eat environment name
1134                                 string const name = p.getArg('{', '}');
1135                                 if (name != curr_env())
1136                                         p.error("\\end{" + name + "} does not match \\begin{"
1137                                                 + curr_env() + "}");
1138                                 active_environments.pop();
1139                                 return;
1140                         }
1141                         p.error("found 'end' unexpectedly");
1142                 }
1143
1144                 else if (t.cs() == "item")
1145                         handle_par(os);
1146
1147                 else if (t.cs() == ")") {
1148                         if (flags & FLAG_SIMPLE2)
1149                                 return;
1150                         p.error("found '\\)' unexpectedly");
1151                 }
1152
1153                 else if (t.cs() == "]") {
1154                         if (flags & FLAG_EQUATION)
1155                                 return;
1156                         p.error("found '\\]' unexpectedly");
1157                 }
1158
1159                 else if (t.cs() == "documentclass") {
1160                         vector<string> opts;
1161                         split(p.getArg('[', ']'), opts, ',');
1162                         handle_opt(opts, known_languages, h_language);
1163                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1164                         h_quotes_language = h_language;
1165                         h_options = join(opts, ",");
1166                         h_textclass = p.getArg('{', '}');
1167                 }
1168
1169                 else if (t.cs() == "usepackage") {
1170                         string const options = p.getArg('[', ']');
1171                         string const name = p.getArg('{', '}');
1172                         if (options.empty() && name.find(',')) {
1173                                 vector<string> vecnames;
1174                                 split(name, vecnames, ',');
1175                                 vector<string>::const_iterator it  = vecnames.begin();
1176                                 vector<string>::const_iterator end = vecnames.end();
1177                                 for (; it != end; ++it)
1178                                         handle_package(trim(*it), string());
1179                         } else {
1180                                 handle_package(name, options);
1181                         }
1182                 }
1183
1184                 else if (t.cs() == "def") {
1185                         string name = p.getToken().cs();
1186                         while (p.nextToken().cat() != catBegin)
1187                                 name += p.getToken().asString();
1188                         handle_ert(os, "\\def\\" + name + '{' + p.verbatimItem() + '}');
1189                 }
1190
1191                 else if (t.cs() == "par")
1192                         handle_par(os);
1193
1194                 else if (is_known(t.cs(), known_headings)) {
1195                         string name = t.cs();
1196                         if (p.nextToken().asInput() == "*") {
1197                                 p.getToken();
1198                                 name += "*";
1199                         }
1200                         os << "\n\n\\layout " << cap(name) << "\n\n";
1201                         string opt = p.getOpt();
1202                         if (opt.size()) {
1203                                 begin_inset(os, "OptArg\n");
1204                                 os << "collapsed true\n\n\\layout Standard\n\n" << opt;
1205                                 end_inset(os);
1206                         }
1207                         parse(p, os, FLAG_ITEM, mode, outer);
1208                         os << "\n\n\\layout Standard\n\n";
1209                 }
1210
1211                 else if (t.cs() == "includegraphics") {
1212                         if (mode == TEXT_MODE) {
1213                                 map<string, string> opts = split_map(p.getArg('[', ']'));
1214                                 string name = p.verbatimItem();
1215                                 begin_inset(os, "Graphics ");
1216                                 os << "\n\tfilename " << name << '\n';
1217                                 if (opts.find("width") != opts.end())
1218                                         os << "\twidth " << opts["width"] << '\n';
1219                                 if (opts.find("height") != opts.end())
1220                                         os << "\theight " << opts["height"] << '\n';
1221                                 end_inset(os);
1222                         } else {
1223                                 os << "\\includegraphics ";
1224                         }
1225                 }
1226                 
1227                 else if (t.cs() == "footnote") {
1228                         begin_inset(os, "Foot\n");
1229                         os << "collapsed true\n\n\\layout Standard\n\n";
1230                         parse(p, os, FLAG_ITEM, mode, false);
1231                         end_inset(os);
1232                 }
1233
1234                 else if (t.cs() == "makeindex" || t.cs() == "maketitle")
1235                         ; // swallow this
1236
1237                 else if (t.cs() == "tableofcontents")
1238                         p.verbatimItem(); // swallow this
1239
1240                 else if (t.cs() == "hline" && mode == TABLE_MODE)
1241                         hlines += "\\hline";
1242
1243                 else if (t.cs() == "cline" && mode == TABLE_MODE)
1244                         hlines += "\\cline{" + p.verbatimItem() + '}';
1245
1246                 else if (t.cs() == "tiny" && mode == TEXT_MODE)
1247                         os << "\n\\size tiny\n";
1248
1249                 else if (t.cs() == "scriptsize" && mode == TEXT_MODE)
1250                         os << "\n\\size scriptsize\n";
1251
1252                 else if (t.cs() == "Large" && mode == TEXT_MODE)
1253                         os << "\n\\size larger\n";
1254
1255                 else if (t.cs() == "textrm") {
1256                         if (mode == TEXT_MODE) {
1257                                 os << "\n\\family roman\n";
1258                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1259                                 os << "\n\\family default\n";
1260                         } else {
1261                                 os << '\\' << t.cs() << '{';
1262                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1263                                 os << '}';
1264                         }
1265                 }
1266
1267                 else if (t.cs() == "textsf") {
1268                         if (mode == TEXT_MODE) {
1269                                 os << "\n\\family sans\n";
1270                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1271                                 os << "\n\\family default\n";
1272                         } else {
1273                                 os << '\\' << t.cs() << '{';
1274                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1275                                 os << '}';
1276                         }
1277                 }
1278
1279                 else if (t.cs() == "texttt") {
1280                         if (mode == TEXT_MODE) {
1281                                 os << "\n\\family typewriter\n";
1282                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1283                                 os << "\n\\family default\n";
1284                         } else {
1285                                 os << '\\' << t.cs() << '{';
1286                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1287                                 os << '}';
1288                         }
1289                 }
1290
1291                 else if (t.cs() == "textsc") {
1292                         if (mode == TEXT_MODE) {
1293                                 os << "\n\\noun on\n";
1294                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1295                                 os << "\n\\noun default\n";
1296                         } else {
1297                                 os << '\\' << t.cs() << '{';
1298                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1299                                 os << '}';
1300                         }
1301                 }
1302
1303                 else if (t.cs() == "textbf") {
1304                         if (mode == TEXT_MODE) {
1305                                 os << "\n\\series bold\n";
1306                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1307                                 os << "\n\\series default\n";
1308                         } else {
1309                                 os << '\\' << t.cs() << '{';
1310                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1311                                 os << '}';
1312                         }
1313                 }
1314
1315                 else if (t.cs() == "underbar") {
1316                         if (mode == TEXT_MODE) {
1317                                 os << "\n\\bar under\n";
1318                                 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1319                                 os << "\n\\bar default\n";
1320                         } else {
1321                                 os << '\\' << t.cs() << '{';
1322                                 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1323                                 os << '}';
1324                         }
1325                 }
1326
1327                 else if ((t.cs() == "emph" || t.cs() == "noun") && mode == TEXT_MODE) {
1328                         os << "\n\\" << t.cs() << " on\n";
1329                         parse(p, os, FLAG_ITEM, mode, outer);
1330                         os << "\n\\" << t.cs() << " default\n";
1331                 }
1332
1333                 else if (t.cs() == "mbox" && mode != TEXT_MODE) {
1334                         os << "\n\\mbox{";
1335                         parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1336                         os << '}';
1337                 }
1338
1339                 else if (is_known(t.cs(), known_latex_commands) && mode == TEXT_MODE) {
1340                         begin_inset(os, "LatexCommand ");
1341                         os << '\\' << t.cs();
1342                         os << p.getOpt();
1343                         os << p.getOpt();
1344                         os << '{' << p.verbatimItem() << '}';
1345                         end_inset(os);
1346                 }
1347
1348                 else if (t.cs() == "bibitem") {
1349                         os << "\n\\layout Bibliography\n\\bibitem ";
1350                         os << p.getOpt();
1351                         os << '{' << p.verbatimItem() << '}' << "\n\n";
1352                 }
1353
1354                 else if (mode == TEXT_MODE && is_known(t.cs(), known_quotes)) {
1355                   char const ** where = is_known(t.cs(), known_quotes);
1356                         begin_inset(os, "Quotes ");
1357                         os << known_coded_quotes[where - known_quotes];
1358                         end_inset(os);
1359                 }
1360
1361                 else if (t.cs() == "LyX" && mode == TEXT_MODE) {
1362                         p.verbatimItem(); // eat {}
1363                         os << "LyX";
1364                 }
1365
1366                 else if (t.cs() == "TeX" && mode == TEXT_MODE) {
1367                         p.verbatimItem(); // eat {}
1368                         os << "TeX";
1369                 }
1370
1371                 else if (t.cs() == "LaTeX" && mode == TEXT_MODE) {
1372                         p.verbatimItem(); // eat {}
1373                         os << "LaTeX";
1374                 }
1375
1376                 else if (t.cs() == "LaTeXe" && mode == TEXT_MODE) {
1377                         p.verbatimItem(); // eat {}
1378                         os << "LaTeXe";
1379                 }
1380
1381                 else if (t.cs() == "lyxarrow" && mode == TEXT_MODE) {
1382                         p.verbatimItem();
1383                         os << "\\SpecialChar \\menuseparator\n";
1384                 }
1385
1386                 else if (t.cs() == "ldots" && mode == TEXT_MODE) {
1387                         p.verbatimItem();
1388                         os << "\\SpecialChar \\ldots{}\n";
1389                 }
1390
1391                 else if (t.cs() == "@" && mode == TEXT_MODE)
1392                         os << "\\SpecialChar \\@";
1393
1394                 else if (t.cs() == "textasciitilde" && mode == TEXT_MODE)
1395                         os << '~';
1396
1397                 else if (t.cs() == "_" && mode == TEXT_MODE)
1398                         os << '_';
1399
1400                 else if (t.cs() == "&" && mode == TEXT_MODE)
1401                         os << '&';
1402
1403                 else if (t.cs() == "#" && mode == TEXT_MODE)
1404                         os << "#";
1405
1406                 else if (t.cs() == "\"") {
1407                         string const name = p.verbatimItem();
1408                              if (name == "a") os << 'ä';
1409                         else if (name == "o") os << 'ö';
1410                         else if (name == "u") os << 'ü';
1411                         else if (name == "A") os << 'Ä';
1412                         else if (name == "O") os << 'Ö';
1413                         else if (name == "U") os << 'Ü';
1414                         else handle_ert(os, "\"{" + name + "}");
1415                 }
1416
1417                 else if (t.cs() == "ss")
1418                         os << "ß";
1419
1420                 else if (t.cs() == "input")
1421                         handle_ert(os, "\\input{" + p.verbatimItem() + "}\n");
1422
1423                 else if (t.cs() == "fancyhead") {
1424                         ostringstream ss;
1425                         ss << "\\fancyhead";
1426                         ss << p.getOpt();
1427                         ss << '{' << p.verbatimItem() << "}\n";
1428                         handle_ert(os, ss.str());
1429                 }
1430
1431                 else {
1432                         //cerr << "#: " << t << " mode: " << mode << endl;
1433                         if (mode == TEXT_MODE) {
1434                                 // heuristic: read up to next non-nested space
1435                                 /*
1436                                 string s = t.asInput();
1437                                 string z = p.verbatimItem();
1438                                 while (p.good() && z != " " && z.size()) {
1439                                         //cerr << "read: " << z << endl;
1440                                         s += z;
1441                                         z = p.verbatimItem();
1442                                 }
1443                                 cerr << "found ERT: " << s << endl;
1444                                 handle_ert(os, s + ' ');
1445                                 */
1446                                 handle_ert(os, t.asInput() + ' ');
1447                         } else {
1448                                 os << t.asInput();
1449                                 //cerr << "#: writing: '" << t.asInput() << "'\n";
1450                         }
1451                 }
1452
1453                 if (flags & FLAG_LEAVE) {
1454                         flags &= ~FLAG_LEAVE;
1455                         break;
1456                 }
1457         }
1458 }
1459
1460
1461
1462 } // anonymous namespace
1463
1464
1465 int main(int argc, char * argv[])
1466 {
1467         if (argc <= 1) {
1468                 cerr << "Usage: " << argv[0] << " <infile.tex>" << endl;
1469                 return 2;
1470         }
1471
1472         ifstream is(argv[1]);
1473         Parser p(is);
1474         parse_preamble(p, cout);
1475         active_environments.push("document");
1476         parse(p, cout, FLAG_END, TEXT_MODE, true);
1477         cout << "\n\\the_end";
1478
1479         return 0;
1480 }
1481
1482 // }])