1 /*
2  * Copyright (c) 2017, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 /**
19    \file
20    \brief Various definitions for the utility programs, ilmpt, machar,
21    symutil, and symini.
22 
23    If compiled as C++ code this file provides a definition of the base
24    class for various utility programs that generate documentation and
25    header files for compilers in the build process.
26 
27    Define the preprocessor symbol USE_OLD_C_UTILS *before* including
28    this file if you need the old C interfaces.
29 
30    The NroffInStream, NroffMap, and NroffTokenStream interfaces are always
31    provided.
32  */
33 
34 #ifndef _UTILS_UTILS_H
35 #define _UTILS_UTILS_H
36 
37 #include "universal.h"
38 
39 #if defined(__cplusplus)
40 
41 #include <cassert>
42 #include <cstdlib>
43 #include <fstream>
44 #include <iostream>
45 #include <string>
46 #include <vector>
47 #include <list>
48 #include <map>
49 
50 void collectIncludePaths(int argc, char *argv[]);
51 extern std::list<std::string> includePaths;
52 
53 /// types of errors printError can recognize.
54 enum ErrorSeverity { INFO = 1, WARN, SEVERE, FATAL };
55 
56 /** \brief Similar to an std::ifstream, but intended for reading nroff files.
57    Self-closing when end of input is reached.  Nroff include directives
58    (.so "foo.n") are handled automatically. */
59 class NroffInStream
60 {
61   /* Context used for processing an input file. */
62   struct context {
63     std::ifstream *file;  /**< file descriptor */
64     std::string filename; /**< file name */
65     int lineno;           /**< file line number */
contextcontext66     context(const char *filename_)
67         : file(new std::ifstream(filename_)), filename(filename_), lineno(0)
68     {
69     }
70   };
71 
72   //! Stack of open files.
73   std::vector<context> stack;
74 
75   //! Top of stack
76   context &
tos()77   tos()
78   {
79     return stack.back();
80   }
81 
82   void
push_file(const char * filename)83   push_file(const char *filename)
84   {
85     stack.push_back(context(filename));
86   }
87 
88   void pop_file();
89 
90 public:
91   /** Open file.  Use operator!() to determine if file was successfully opened.
92    */
93   void
open(const char * filename)94   open(const char *filename)
95   {
96     assert(stack.empty());
97     push_file(filename);
98   }
99 
100   /** Close all files.  No-op if file is not open. */
101   void close();
102 
103   /**
104      \brief print an error message with a line number if non-zero and
105      the error severity level. Exit if the error is FATAL.
106 
107      \param sev error severity;
108      \param txt error text to be written;
109      \param additional text to be appended to the error message.
110      \return this function doesn't return if the error severity is
111      FATAL, instead it calls exit() to terminate the utility program.
112   */
113   void printError(ErrorSeverity sev, const char *txt1, const char *txt2 = "");
114 
115   /** True if error occurred for innermost file.  This method exists to simplify
116       replacing uses of std::ifstream with NroffInStream. */
117   bool operator!()
118   {
119     return tos().file->operator!();
120   }
121 
122   friend bool getline(NroffInStream &f, std::string &s);
123 
~NroffInStream()124   ~NroffInStream()
125   {
126     close();
127   }
128 };
129 
130 //! Get line of input from the given stream.
131 bool getline(NroffInStream &f, std::string &s);
132 
133 /** \brief Map from nroff commands to integers.
134     This class is used in conjunction with class NroffTokenStream. */
135 class NroffMap
136 {
137   /** Note: with C++11, a std::unordered_map could be used instead, but
138       the time savings are probably neglible. */
139   std::map<std::string, int> myMap;
140 
141 public:
142   /** Helper class that enables using subscript syntax to initialize an
143      NroffMap.  Clients should not mention the name 'proxy'. */
144   class proxy
145   {
146     NroffMap *map;
147     const char *string;
148 
proxy(NroffMap * map_,const char * string_)149     proxy(NroffMap *map_, const char *string_) : map(map_), string(string_)
150     {
151     }
152 
153     friend class NroffMap;
154 
155   public:
156     //! See comments for NroffMap::operator[]
157     void operator=(int code);
158   };
159 
160   /** Return proxy that can be used to assign a code to an nroffPrimitive.
161       an NroffMap. E.g.:
162 
163       NroffMap m;
164       m[".xy"] = 42;
165 
166       The string must have three characters, and the code must be non-zero.
167       These restrictions are checked by proxy::operator=.
168       */
169   proxy operator[](const char *nroffPrimitive)
170   {
171     return proxy(this, nroffPrimitive);
172   }
173 
174   /** If current line begins with one of the strings in this map,
175       return the corresponding code.  Otherwise return 0. */
176   int match(const std::string &line) const;
177 
178   /** Given a code, find the corresponding string.
179       Return nullptr code not found. */
180   const char *stringOf(int code);
181 };
182 
183 /** Class for reading nroff tokens from a file.
184     Like NroffInStream, it is self-closing. */
185 class NroffTokenStream
186 {
187   NroffInStream ifs;
188   std::string buf;
189   const char *pos; //!< pointer into buf
190 
191 public:
192   //! Create NroffTokenStream for a file.
NroffTokenStream(const char * filename)193   NroffTokenStream(const char *filename) : pos(nullptr)
194   {
195     ifs.open(filename);
196   }
197   /** Advance to next line that has a map code.  Return 0 if none found. */
198   int get_line(const NroffMap &map);
199 
200   /** Get next token on curent line. */
201   bool get_token(std::string &tok);
202 
203   void
printError(ErrorSeverity sev,const char * txt1)204   printError(ErrorSeverity sev, const char *txt1)
205   {
206     ifs.printError(sev, txt1);
207   }
208 };
209 
210 #endif /* defined(__cplusplus) */
211 
212 #if defined(__cplusplus) && !defined(USE_OLD_C_UTILS)
213 #include <cstdio>
214 #include <algorithm>
215 #include <iomanip>
216 #include <iterator>
217 #include <sstream>
218 
219 #define LT_UNK -1
220 #define LT_EOF 0
221 
222 /**
223    \class Manages conversion of NROFF input to Sphinx format.
224  */
225 class SphinxConverter
226 {
227 
228   typedef std::vector<std::string> cell_type;
229   typedef std::vector<cell_type> table_row_type;
230 
231   static const bool DEBUG = false;
232   /// Sphinx convention specifies which character is used for underlining
233   /// headers of various levels.  This array maps header levels to the specific
234   /// character.
235   char heading[6];
236   std::stringstream source; ///< entire input file read into the stream buffer.
237   std::stringstream target; ///< a buffer to hold the generated RST.
238   std::ofstream ofs;        ///< Sphinx output file stream.
239   std::vector<std::string> tokens;
240   std::vector<std::string> listStack;
241   bool op_started;
242   int lineno;
243   bool genRST; ///< If set, generate RST. Otherwise, just eat lines.
244 public:
245   /**
246      \brief Initializes the internal data structures.
247    */
SphinxConverter()248   SphinxConverter()
249   {
250     heading[0] = '#';
251     heading[1] = '*';
252     heading[2] = '=';
253     heading[3] = '-';
254     heading[4] = '^';
255     heading[5] = '"';
256     genRST = true;
257   }
258 
259   /**
260      \brief Write the last RST file.
261    */
~SphinxConverter()262   ~SphinxConverter()
263   {
264     if (ofs.is_open()) {
265       generateRST();
266       ofs.close();
267     }
268   }
269 
270   /**
271      \brief Create the Sphinx output file.
272    */
273   void
setOutputFile(const std::string & s)274   setOutputFile(const std::string &s)
275   {
276     if (ofs.is_open()) {
277       generateRST();
278       ofs.close();
279     }
280     ofs.open(s.c_str());
281     if (!ofs) {
282       std::cerr << "Can't create file " << s << '\n';
283       exit(1);
284     }
285     source.str("");
286     source.clear();
287     target.str("");
288     target.clear();
289     listStack.clear();
290     op_started = false;
291     lineno = 0;
292   }
293 
294   /**
295      \brief Collect a single line of an input file and store in a string stream
296      for running the conversion process later on the entire source file.
297    */
298   void
process(const std::string & s)299   process(const std::string &s)
300   {
301     int startPos = 0;
302     int len, dirLen;
303 
304     dirLen = chkCommentString(s);
305     if (dirLen || !genRST) {
306       len = s.length();
307       if (dirLen == 2 && len >= 12 &&
308           ((s.substr(2, 9)).compare("RST_ONLY ")) == 0) {
309         /// found \#RST_ONLY directive
310         startPos = 11;
311       } else if (dirLen == 4 && len >= 13 &&
312                  ((s.substr(4, 9)).compare("RST_ONLY ")) == 0) {
313         /// found .\"#RST_ONLY directive
314         startPos = 13;
315       } else {
316         return;
317       }
318     }
319     auto r = (startPos == 0)
320                  ? escapeInlineMarkup(cutTrailingSpace(s))
321                  : escapeInlineMarkup(cutTrailingSpace(s.substr(startPos)));
322     source << r // join lines if new line is escaped with backslash
323            << (r.empty() || r[r.length() - 1] != '\\' ? '\n' : ' ');
324   }
325 
326 private:
327   /**
328     \brief Check to see if an input line is a special directive that begins
329            with either a \# NROFF comment or an NROFF comment that begins with
330            a .\" followed a # character.
331 
332      The \#NO_RST directive is used to turn off RST generation and the
333      \#END_NO_RST directive is used to reenable RST generation.
334      The .\"#NO_RST and .\"#END_NO_RST directives are also recognized.
335 
336      \param s is the string to process
337 
338      \returns 2 for \# style directive, 4 for .\"# style directive, 0 for no
339       directive.
340   */
341   int
chkCommentString(const std::string & s)342   chkCommentString(const std::string &s)
343   {
344     int len, pos, dirLen;
345     bool toggleGenRST;
346 
347     len = s.length();
348 
349     if (len >= 2 && s[0] == '\\' && s[1] == '#') {
350       dirLen = 2;
351     } else if (len >= 3 && s[0] == '.' && s[1] == '\\' && s[2] == '\"') {
352       dirLen = 4;
353     } else {
354       dirLen = 0;
355     }
356 
357     if (dirLen > 0) {
358       /// We have an NROFF \# or .\" comment
359       if (dirLen == 4 && len >= 4 && s[3] == '#') {
360         pos = 2;
361       } else {
362         pos = 0;
363       }
364 
365       if (len >= 8) {
366         /// Check for special \#NO_RST or \#END_NO_RST directive in comment
367         /// or .\"#NO_RST or .\"#END_NO_RST directive in comment`
368         if (len >= 12 && s[dirLen] == 'E' && s[dirLen + 1] == 'N' &&
369             s[dirLen + 2] == 'D' && s[dirLen + 3] == '_') {
370           pos += 6;
371           toggleGenRST = true;
372         } else {
373           pos += 2;
374           toggleGenRST = false;
375         }
376         std::string str2 = s.substr(pos, 6);
377         if (str2.compare("NO_RST") == 0) {
378           genRST = toggleGenRST;
379         }
380       }
381     }
382     return dirLen;
383   }
384 
385   /**
386      \brief Generate an output file in RST format.
387    */
388   void
generateRST()389   generateRST()
390   {
391     if (!ofs.is_open()) {
392       return;
393     }
394     expandDefinedStrings();
395     for (std::string s; std::getline(source, s); ++lineno) {
396       parseOneLineRequest(s, source, target, true);
397     }
398     ofs << target.str();
399   }
400 
401   /**
402      \brief Convert a line in NROFF input to a Sphinx formatted line.
403    */
404   void
405   parseOneLineRequest(const std::string &s, std::stringstream &src,
406                       std::stringstream &tgt, bool newline = false)
407   {
408     if (DEBUG)
409       tgt << "\n..\n  Line " << lineno << " <" << s << ">\n";
410     if (s.empty()) {
411       if (DEBUG)
412         tgt << "\n..\n  EMPTY\n";
413       return;
414     }
415     // comments
416     if (s.substr(0, 2) == "\\\"" || s.substr(0, 3) == ".\\\"") {
417       if (DEBUG)
418         tgt << "\n..\n  COMMENT\n";
419       return;
420     }
421     if (s.substr(0, 2) == "\\&") {
422       tgt << s.substr(2) << '\n';
423       return;
424     }
425     if (s[0] != '.') {
426       if (op_started) {
427         tgt << '\n';
428         op_started = false;
429       }
430       tgt << s << '\n';
431       return;
432     }
433     tokenize(s);
434     if (tokens.empty()) {
435       tgt << s;
436       return;
437     }
438     auto tag = tokens[0];
439     if (DEBUG)
440       tgt << "\n..\n  Tag <" << tag << ">\n";
441     if (tag == ".de") {
442       for (std::string s; std::getline(src, s) && s.substr(0, 2) != "..";
443            ++lineno) {
444       }
445       ++lineno;
446       return;
447     }
448     if (tag != ".OP") {
449       op_started = false;
450     }
451     if (tag == ".NS") {
452       auto title = tokens.size() > 3 ? tokens[3] + tokens[2] : tokens[2];
453       std::string lining(title.length(), '*');
454       tgt << "\n\n" << lining << '\n' << title << '\n' << lining << '\n';
455       return;
456     }
457     if (tag == ".sh") {
458       auto level = atoi(tokens[1].c_str());
459       std::string lining(tokens[2].length(), heading[level]);
460       tgt << "\n\n" << lining << '\n' << tokens[2] << '\n' << lining << '\n';
461       return;
462     }
463     if (tag == ".SM") {
464       if (tokens[1] == "E") {
465         return;
466       }
467       std::ostringstream ss;
468       ss << tokens[1];
469       for (std::vector<std::string>::const_iterator it = tokens.begin() + 2,
470                                                     E = tokens.end();
471            it != E; ++it) {
472         ss << ", " << *it;
473       }
474       std::string lining(ss.str().length(), heading[4]);
475       tgt << "\n\n" << lining << '\n' << ss.str() << '\n' << lining << '\n';
476       return;
477     }
478     if (tag == ".OP") {
479       if (!op_started) {
480         op_started = true;
481         tgt << "\n.. code-block:: none\n\n";
482       }
483       tgt << "  ";
484       for (std::vector<std::string>::const_iterator it = tokens.begin() + 1,
485                                                     E = tokens.end();
486            it != E; ++it) {
487         tgt << " " << *it;
488       }
489       tgt << "\n";
490       return;
491     }
492     // a list begins
493     if (tag == ".ba" || // augments the base indent by n.
494         tag == ".ip" || tag == ".np" || tag == ".BL" || tag == ".CL" ||
495         tag == ".CP" || tag == ".DE" || tag == ".FL" || tag == ".GN" ||
496         tag == ".H1" || tag == ".H2" || tag == ".H3" || tag == ".H4" ||
497         tag == ".H5" || tag == ".H6" || tag == ".H7" || tag == ".H8" ||
498         tag == ".H9" || tag == ".Ik" || tag == ".IL" || tag == ".IN" ||
499         tag == ".IP" || tag == ".OL" || tag == ".OC" || tag == ".OV" ||
500         tag == ".PD" || tag == ".Sc" || tag == ".SE" || tag == ".SF" ||
501         tag == ".ST" || tag == ".TY" || tag == ".XF") {
502       src.seekg(-s.length() - 1, std::ios_base::cur);
503       parseList(src, tgt);
504       return;
505     }
506     // a table begins
507     if (tag == ".TS") {
508       parseTable(src, tgt);
509       return;
510     }
511     // a code block begins
512     if (tag == ".CS") {
513       parseCodeBlock(src, tgt);
514       return;
515     }
516     // a verbatim block begins
517     if (tag == ".nf") {
518       parseLineBlock(src, tgt);
519       return;
520     }
521     if (tag == ".us" || tag == ".US") {
522       if (tokens.size() > 1) {
523         tgt << "\n*" << tokens[1];
524         for (std::vector<std::string>::const_iterator it = tokens.begin() + 2,
525                                                       E = tokens.end();
526              it != E; ++it) {
527           tgt << ' ' << *it;
528         }
529         tgt << "* --- ";
530       }
531       return;
532     }
533     if (tag == ".XB") {
534       if (tokens.size() > 1) {
535         tgt << "\n**" << tokens[1];
536         for (std::vector<std::string>::const_iterator it = tokens.begin() + 2,
537                                                       E = tokens.end();
538              it != E; ++it) {
539           tgt << ' ' << *it;
540         }
541         tgt << "**\n";
542       }
543       return;
544     }
545     if (tag == ".b") {
546       generateHighlightedItem("**", tgt);
547       return;
548     }
549     if (tag == ".cw" || tag == ".MA" || tag == ".NM") {
550       generateHighlightedItem("``", tgt);
551       return;
552     }
553     if (tag == ".i") {
554       generateHighlightedItem("*", tgt);
555       return;
556     }
557     if (tag == ".q") {
558       generateHighlightedItem("\"", tgt);
559       return;
560     }
561     if (tag == ".DN") {
562       if (tokens.size() > 1) {
563         tgt << "\n``" << tokens[1] << "``\n";
564       }
565       return;
566     }
567     if (tag == ".DA") {
568       if (tokens.size() > 1) {
569         tgt << "   ``" << tokens[1] << "``\n";
570       }
571       return;
572     }
573     if (tag == ".AT") {
574       if (tokens.size() > 1) {
575         tgt << "\n*Attributes*:";
576         for (std::vector<std::string>::const_iterator it = tokens.begin() + 1,
577                                                       E = tokens.end();
578              it != E; ++it) {
579           tgt << " " << *it;
580         }
581         tgt << "\n";
582       }
583       return;
584     }
585     if (tag == ".SI") {
586       if (tokens.size() > 1) {
587         tgt << "*" << tokens[1] << "*";
588         for (std::vector<std::string>::const_iterator it = tokens.begin() + 2,
589                                                       E = tokens.end();
590              it != E; ++it) {
591           tgt << " " << *it;
592         }
593         tgt << "\n\n";
594       }
595       return;
596     }
597     if (tag == ".ul") {
598       std::string t;
599       if (std::getline(src, t)) {
600         tgt << '*' << t << "*\n";
601       }
602       return;
603     }
604     if (tag == ".lp" || tag == ".br") {
605       tgt << '\n';
606       return;
607     }
608     if (tag == ".(b" || tag == ".)b") {
609       // FIXME: Couldn't find what .(b .)b pair stands for.
610       //        It's not defined in groff_me macro package.
611       // tgt << '\n';
612       return;
613     }
614     if (tag == ".(z" || tag == ".)z") {
615       // FIXME: .(z .)z means floating in groff_me.
616       //        How to represent this in RST?
617       return;
618     }
619     if (tag == ".bp" || // begin new page
620         tag == ".ce" || // FIXME: center next n lines. Not needed.
621         tag == ".EP" || //
622         tag == ".ft" || // font
623         tag == ".hl" || // FIXME: horizontal line. Not needed.
624         tag == ".ne" || // FIXME: can't find what this request does
625         tag == ".nr" || //
626         tag == ".re" || // Reset tabs to default values.
627         tag == ".sp" || //
628         tag == ".sz" || // Augment the point size by n points.
629         tag == ".ta") { //
630       return;
631     }
632     tgt << s << (newline ? "\n" : "");
633   }
634 
635   void
parseList(std::stringstream & src,std::stringstream & tgt)636   parseList(std::stringstream &src, std::stringstream &tgt)
637   {
638     std::string s;
639     if (!std::getline(src, s)) {
640       return;
641     }
642     tokenize(s);
643     auto tag = tokens[0];
644     if (tag == ".FL" || tag == ".CL" || tag == ".OL") {
645       listStack.push_back(".IL");
646     } else {
647       listStack.push_back(tag);
648     }
649     // format the list item header
650     std::stringstream ss;
651     if (DEBUG)
652       ss << "\n..\n  Start list <" << s << ">\n";
653     if (tag == ".BL") {
654       ss << "*  ";
655     } else if (tag == ".OV") {
656       if (tokens.size() > 2) {
657         ss << "``" << tokens[1] << " (" << tokens[2] << ")``\n";
658       }
659     } else if (tag == ".CL" || tag == ".FL" || tag == ".IL" || tag == ".OL" ||
660                tag == ".CP") {
661       if (tokens.size() < 4) {
662         ss << "``" << tokens[1] << "``\n";
663       } else if (tokens.size() > 3) {
664         ss << "#. **" << tokens[1] << "**";
665         for (std::vector<std::string>::size_type it = 3; it < tokens.size();
666              ++it) {
667           ss << " " << tokens[it];
668         }
669         ss << "    *Type*: *" << tokens[2] << "*\n\n";
670       }
671     } else if (tag == ".TY" || tag == ".PD") {
672       if (tokens.size() > 1 && tokens[1] != "B" && tokens[1] != "E") {
673         std::vector<std::string>::const_iterator it = tokens.begin() + 1,
674                                                  E = tokens.end();
675         ss << "``" << *it++ << "``";
676         if (it != E) {
677           ss << " ``" << *it++ << "``";
678           for (; it != E; ++it) {
679             ss << " " << *it;
680           }
681         }
682       }
683       ss << '\n';
684     } else if (tag == ".IN" || tag == ".GN") {
685       if (tokens.size() > 1) {
686         for (std::vector<std::string>::const_iterator it = tokens.begin() + 1,
687                                                       E = tokens.end();
688              it != E; ++it) {
689           ss << "``" << *it << "`` ";
690         }
691       }
692     } else if (tag == ".ip") {
693       if (tokens.size() > 1) {
694         ss << tokens[1] << '\n';
695       } else {
696         ss << "*  ";
697       }
698     } else if (tag == ".np") {
699       ss << "#. ";
700     } else if (tag == ".ba") {
701       ss << '\n';
702     } else if (!(tokens.size() > 1 &&
703                  (tag == ".Ik" || tag == ".OC" || tag == ".Ik" ||
704                   tag == ".ST" || tag == ".SF" || tag == ".Sc") &&
705                  (tokens[1] == "B" || tokens[1] == "E"))) {
706       if (tokens.size() > 1) {
707         ss << "``" << tokens[1] << "``\n";
708       }
709     }
710     // consume the lines up to the next list item, possibly nested
711     while (std::getline(src, s)) {
712       if (s.empty()) {
713         ss << '\n';
714         continue;
715       }
716       tokenize(s);
717       auto tag = tokens[0];
718       if (tag == ".sh" || tag == ".lp" || tag == ".SM") {
719         src.seekg(-s.length() - 1, std::ios_base::cur);
720         generateListItem(ss, tgt);
721         listStack.clear();
722         return;
723       } else if (tag == ".ip" || tag == ".np" || tag == ".BL" || tag == ".CL" ||
724                  tag == ".CP" || tag == ".DE" || tag == ".FL" || tag == ".GN" ||
725                  tag == ".H1" || tag == ".H2" || tag == ".H3" || tag == ".H4" ||
726                  tag == ".H5" || tag == ".H6" || tag == ".H7" || tag == ".H8" ||
727                  tag == ".H9" || tag == ".Ik" || tag == ".IL" || tag == ".IN" ||
728                  tag == ".IP" || tag == ".OL" || tag == ".OC" || tag == ".OV" ||
729                  tag == ".PD" || tag == ".Sc" || tag == ".SE" || tag == ".SF" ||
730                  tag == ".ST" || tag == ".TY" || tag == ".XF") {
731         if (tag == ".FL" || tag == ".CL" || tag == ".OL") {
732           tag = ".IL";
733         }
734         auto it = listStack.begin();
735         for (auto E = listStack.end(); it != E; ++it) {
736           if (tag == *it) {
737             listStack.erase(it, E);
738             src.seekg(-s.length() - 1, std::ios_base::cur);
739             generateListItem(ss, tgt);
740             return;
741           }
742         }
743         listStack.push_back(tag);
744         ss << '\n';
745         src.seekg(-s.length() - 1, std::ios_base::cur);
746         parseList(src, ss);
747         ss << '\n';
748       } else if (tag == ".ba") {
749         if (tokens.size() > 1) {
750           if (tokens[1][0] == '+') {
751             src.seekg(-s.length() - 1, std::ios_base::cur);
752             parseList(src, ss);
753           } else {
754             generateListItem(ss, tgt);
755             return;
756           }
757         }
758         ss << '\n';
759       } else {
760         parseOneLineRequest(s, src, ss);
761       }
762     }
763     generateListItem(ss, tgt);
764   }
765 
766   void
parseTable(std::stringstream & src,std::stringstream & tgt)767   parseTable(std::stringstream &src, std::stringstream &tgt)
768   {
769     std::ostringstream oss;
770     for (std::string s; std::getline(src, s);) {
771       auto pos = s.find_first_of(" \t");
772       auto tag = s.substr(0, pos);
773       if (tag == ".TE") {
774         if (DEBUG)
775           tgt << "\n..\n  END OF TABLE\n";
776         generateTable(oss.str(), tgt);
777         return;
778       } else {
779         if (DEBUG)
780           tgt << "\n..\n  STORE THE LINE FOR FUTURE\n";
781         if (!s.empty()) {
782           oss << s << '\n';
783         }
784       }
785     }
786   }
787 
788   void
parseCodeBlock(std::stringstream & src,std::stringstream & tgt)789   parseCodeBlock(std::stringstream &src, std::stringstream &tgt)
790   {
791     tgt << "\n.. code-block:: none\n\n";
792     for (std::string s; getline(src, s);) {
793       if (s == ".CE") {
794         tgt << '\n';
795         return;
796       }
797       if (s.empty()) {
798         tgt << '\n';
799       } else if (s.substr(0, 2) == "\\&") {
800         tgt << "   " << s.substr(2) << '\n';
801       } else {
802         tgt << "   " << s << '\n';
803       }
804     }
805   }
806 
807   void
parseLineBlock(std::stringstream & src,std::stringstream & tgt)808   parseLineBlock(std::stringstream &src, std::stringstream &tgt)
809   {
810     tgt << "\n.. line-block::\n";
811     for (std::string s; std::getline(src, s);) {
812       auto pos = s.find_first_of(" \t");
813       auto tag = s.substr(0, pos);
814       if (tag == ".fi") {
815         tgt << '\n';
816         return;
817       } else {
818         if (!s.empty()) {
819           tgt << "    " << s;
820         }
821         tgt << '\n';
822       }
823     }
824   }
825 
826   /**
827      \brief Create a new string without any trailing white space from
828      the given input string.
829 
830      \param s the input string.
831      \return a copy of \a s without any trailing space.
832    */
833   std::string
cutTrailingSpace(const std::string & s)834   cutTrailingSpace(const std::string &s) const
835   {
836     if (s.empty()) {
837       return s;
838     }
839     auto pos = s.find_last_not_of(" \t");
840     // s is not empty therefore it must contain only white space
841     if (pos == std::string::npos) {
842       return std::string();
843     }
844     // anything after pos, if exists, must be white space, so cut it.
845     return s.substr(0, pos + 1);
846   }
847 
848   std::string
escapeInlineMarkup(const std::string & s)849   escapeInlineMarkup(const std::string &s) const
850   {
851     if (s.empty()) {
852       return s;
853     }
854     std::string r;
855     for (std::string::const_iterator c = s.begin(), E = s.end(); c != E; ++c) {
856       if (*c == '*' || *c == '`' ||
857           (*c == '_' &&
858            (c + 1 == E || *(c + 1) == '\'' || *(c + 1) == '\t' ||
859             *(c + 1) == ' ' || *(c + 1) == '"' || *(c + 1) == ',' ||
860             *(c + 1) == '.' || *(c + 1) == ';' || *(c + 1) == ':' ||
861             *(c + 1) == ')'))) {
862         r.append(1, '\\');
863       } else if (*c == '\\' && c + 1 != E && *(c + 1) == ' ') {
864         continue;
865       }
866       r.append(1, *c);
867     }
868     return r;
869   }
870 
871   /**
872      \brief Find and convert strings defined with .ds requests.
873    */
874   void
expandDefinedStrings()875   expandDefinedStrings()
876   {
877     std::ostringstream os;
878     lineno = 1;
879     for (std::string s; getline(source, s);) {
880       s = expandStringInline(s, "\\(bu", "*");
881       s = expandStringInline(s, "\\(em", "---");
882       s = expandStringInline(s, "\\\\*(SC", "**Flang**");
883       s = expandPairedString(s, "\\f(CW", "``", "\\fP", "``");
884       s = expandPairedString(s, "\\\\*(cf", "``", "\\fP", "``");
885       s = expandPairedString(s, "\\\\*(cf", "``", "\\\\*(rf", "``");
886       s = expandPairedString(s, "\\\\*(cr", "``", "\\\\*(rf", "``");
887       s = expandPairedString(s, "\\\\*(mf", "``", "\\\\*(rf", "``");
888       s = expandPairedString(s, "\\\\*(ff", "*", "\\\\*(rf", "*");
889       s = expandPairedString(s, "\\\\*(tf", "*", "\\\\*(rf", "*");
890       s = expandPairedString(s, "\\fI", "*", "\\fP", "*");
891       os << s << '\n';
892     }
893     if (DEBUG)
894       target << "\n..\n  TOTAL LINES: " << lineno << '\n';
895     source.str(os.str());
896     source.clear();
897     lineno = 1;
898   }
899 
900   /**
901      \brief Do simple one to one .ds string expansions for a single line.
902    */
903   std::string
expandStringInline(const std::string & line,const std::string & s,const std::string & r)904   expandStringInline(const std::string &line, const std::string &s,
905                      const std::string &r) const
906   {
907     if (line.empty()) {
908       return line;
909     }
910     auto result = line;
911     auto len = s.length();
912     std::string::size_type pos = 0;
913     while (1) {
914       pos = result.find(s, pos);
915       if (pos == std::string::npos) {
916         break;
917       }
918       result.replace(pos, len, r);
919     }
920     return result;
921   }
922 
923   /**
924      \brief Do paired replacement of markup encoded with .ds strings.
925 
926      For example, fixed font or code is expressed as ``code`` constructs in RST,
927      but Nroff explicitly sets and resets font for a substring of interest,
928      e.g. \*(cfcode\*(rf, where cf and rf are names defined previous with .ds
929      requests.
930    */
931   std::string
expandPairedString(const std::string & line,const std::string & sl,const std::string & rl,const std::string & sr,const std::string & rr)932   expandPairedString(const std::string &line, const std::string &sl,
933                      const std::string &rl, const std::string &sr,
934                      const std::string &rr) const
935   {
936     if (line.empty()) {
937       return line;
938     }
939     auto result = line;
940     auto lenl = sl.length();
941     auto lenr = sr.length();
942     std::string::size_type posl = 0;
943     while (1) {
944       posl = result.find(sl, posl);
945       if (posl == std::string::npos) {
946         break;
947       }
948       auto posr = result.find(sr, posl);
949       if (posr == std::string::npos) {
950         break;
951       }
952       // order of the following two statements is important, DO NOT change.
953       result.replace(posr, lenr, rr);
954       result.replace(posl, lenl, rl); // posr is invalid after this replacement
955     }
956     return result;
957   }
958 
959   void
960   tokenize(const std::string &s,
961            const std::string &separator = std::string(" \t"),
962            const bool allow_empty = false)
963   {
964     tokens.clear();
965     for (std::string::const_iterator head = s.begin(), B = s.begin(),
966                                      E = s.end();
967          head != E;) {
968       for (; head != E && separator.find_first_of(*head) != std::string::npos;
969            ++head) {
970         if (allow_empty) {
971           tokens.push_back(std::string());
972         }
973       }
974       auto tail = head != E ? head + 1 : E;
975       for (; tail != E; ++tail) {
976         if (*head == '"') {
977           if (*tail == '"')
978             break;
979         } else if (separator.find_first_of(*tail) != std::string::npos) {
980           break;
981         }
982       }
983       if (head != E && *head == '"')
984         ++head;
985       tokens.push_back(head == tail ? std::string()
986                                     : s.substr(head - B, tail - head));
987       head = tail == E ? tail : tail + 1;
988       if (allow_empty && head == E && tail != E) {
989         tokens.push_back(std::string());
990       }
991     }
992   }
993 
994   void
generateTable(const std::string & s,std::stringstream & tgt)995   generateTable(const std::string &s, std::stringstream &tgt)
996   {
997     std::string token_separator = "\t";
998     std::istringstream iss(s);
999     std::vector<cell_type> formatting;
1000     std::vector<std::string::size_type> widths;
1001     std::vector<table_row_type> table;
1002     table_row_type::size_type columns;
1003     table_row_type table_row;
1004     cell_type cell;
1005     bool is_multiline = false;
1006     bool end_of_options = false;
1007     // read table formatting options
1008     for (std::string s; std::getline(iss, s);) {
1009       if (DEBUG)
1010         tgt << "\n..\n  OPTIONS Line <" << s << ">\n";
1011       tokenize(s);
1012       if (tokens.empty()) {
1013         continue;
1014       }
1015       auto &last_token = tokens.back();
1016       if (last_token.end()[-1] == ';') {
1017         for (std::vector<std::string>::const_iterator it = tokens.begin(),
1018                                                       E = tokens.end();
1019              it != E; ++it) {
1020           if (it->find_first_of('%') != std::string::npos) {
1021             token_separator = '%';
1022           }
1023         }
1024         continue;
1025       }
1026       // remove '|' tokens
1027       std::vector<std::string> refined_tokens;
1028       for (std::vector<std::string>::iterator it = tokens.begin();
1029            it != tokens.end(); ++it) {
1030         if (DEBUG)
1031           tgt << "\n..\n  TOKEN " << it - tokens.begin() << " <" << *it
1032               << ">\n";
1033         if (*it != "|" && *it != "." && *it != "|.") {
1034           refined_tokens.push_back(*it);
1035         } else if (*it == "." || *it == "|.") {
1036           end_of_options = true;
1037         }
1038       }
1039       formatting.push_back(refined_tokens);
1040       auto &last_refined_token = refined_tokens.back();
1041       if (end_of_options || last_refined_token.end()[-1] == '.') {
1042         if (DEBUG) {
1043           tgt << "\n..\n  FORMATTING SPEC:\n";
1044           for (std::vector<cell_type>::size_type it = 0; it < formatting.size();
1045                ++it) {
1046             tgt << "  " << it;
1047             for (cell_type::const_iterator ci = formatting[it].begin(),
1048                                            E = formatting[it].end();
1049                  ci != E; ++ci) {
1050               tgt << " " << *ci;
1051             }
1052             tgt << '\n';
1053           }
1054         }
1055         break;
1056       }
1057     }
1058     // read table contents
1059     for (std::string s; std::getline(iss, s);) {
1060       if (DEBUG)
1061         tgt << "\n..\n  TBLDATA Line <" << s << ">\n";
1062       if (s == ".TH") {
1063         continue; // skip the running header request
1064       }
1065       if (s == ".T&") {
1066         std::getline(iss, s);
1067         continue; // skip table continue command and the following format opts
1068       }
1069       tokenize(s, token_separator, true);
1070       auto it = tokens.begin();
1071       if (is_multiline) {
1072         if (tokens.empty() || tokens[0] != "T}") {
1073           cell.push_back(s);
1074           continue;
1075         }
1076         table_row.push_back(cell);
1077         is_multiline = false;
1078         cell.clear();
1079         ++it;
1080       }
1081       for (auto E = tokens.end(); it != E; ++it) {
1082         if (*it == "T{") {
1083           is_multiline = true;
1084         } else {
1085           cell.push_back(*it);
1086           table_row.push_back(cell);
1087           cell.clear();
1088         }
1089       }
1090       if (is_multiline) {
1091         continue;
1092       }
1093       table.push_back(table_row);
1094       table_row.clear();
1095     }
1096     // check options consistency
1097     columns = 0;
1098     if (!formatting.empty()) {
1099       for (std::vector<cell_type>::const_iterator it = formatting.begin(),
1100                                                   E = formatting.end();
1101            it != E; ++it) {
1102         if (it->size() > columns) {
1103           columns = it->size();
1104         }
1105       }
1106     }
1107     if (DEBUG)
1108       tgt << "\n..\n  #COLUMNS " << columns << '\n';
1109     // calculate each column width
1110     int row_counter = 0;
1111     bool do_corrections = false;
1112     for (std::vector<table_row_type>::const_iterator r = table.begin(),
1113                                                      rE = table.end();
1114          r != rE; ++r, ++row_counter) {
1115       if (DEBUG)
1116         tgt << "\n..\n  ROW " << std::setw(3) << row_counter;
1117       if (r->size() < columns) {
1118         do_corrections = true;
1119         if (DEBUG)
1120           tgt << '\n';
1121         continue;
1122       }
1123       std::string::size_type column = 0;
1124       for (table_row_type::const_iterator c = r->begin(), cE = r->end();
1125            c != cE; ++c, ++column) {
1126         std::string::size_type width = 0;
1127         for (cell_type::const_iterator s = c->begin(), sE = c->end(); s != sE;
1128              ++s) {
1129           if (s->length() > width) {
1130             width = s->length();
1131           }
1132         }
1133         if (DEBUG) {
1134           tgt << " COL " << column << " (" << std::setw(2) << width << ")";
1135         }
1136         if (column == widths.size()) {
1137           widths.push_back(width);
1138         } else if (widths[column] < width) {
1139           widths[column] = width;
1140         }
1141       }
1142       if (DEBUG)
1143         tgt << '\n';
1144     }
1145     if (DEBUG)
1146       tgt << "\n..\n  WIDTHS SIZE " << widths.size() << '\n';
1147     std::string::size_type table_width = 0;
1148     for (std::vector<std::string::size_type>::iterator it = widths.begin(),
1149                                                        E = widths.end();
1150          it != E; ++it) {
1151       if (*it > 0) {
1152         table_width += *it + 1;
1153       }
1154     }
1155     --table_width;
1156     if (do_corrections) {
1157       for (std::vector<table_row_type>::const_iterator r = table.begin(),
1158                                                        rE = table.end();
1159            r != rE; ++r, ++row_counter) {
1160         if (r->size() == columns) {
1161           continue;
1162         }
1163         std::string::size_type column = 0;
1164         std::string::size_type cumulative_width = 0;
1165         for (table_row_type::const_iterator c = r->begin(), cE = r->end();
1166              c != cE; ++c, ++column) {
1167           std::string::size_type width = 0;
1168           for (cell_type::const_iterator s = c->begin(), sE = c->end(); s != sE;
1169                ++s) {
1170             if (s->length() > width) {
1171               width = s->length();
1172             }
1173           }
1174           if (c + 1 != cE) {
1175             if (widths[column] < width) {
1176               table_width += width - widths[column];
1177               widths[column] = width;
1178             }
1179             cumulative_width += widths[column] + 1;
1180           } else if (cumulative_width + width > table_width) {
1181             table_width += width - widths.back();
1182             widths.back() = width;
1183           }
1184         }
1185       }
1186     }
1187     if (DEBUG) {
1188       tgt << "\n..\n  WIDTHS:";
1189       for (std::vector<std::string::size_type>::const_iterator
1190                it = widths.begin(),
1191                E = widths.end();
1192            it != E; ++it) {
1193         tgt << " " << *it;
1194       }
1195       tgt << '\n';
1196     }
1197     assert(!widths.empty());
1198     // fix up standalone cells with width 0
1199     for (std::vector<table_row_type>::iterator r = table.begin(),
1200                                                rE = table.end();
1201          r != rE; ++r) {
1202       std::vector<std::string::size_type>::const_iterator w = widths.begin();
1203       for (table_row_type::iterator c = r->begin(), cE = r->end(); c != cE;
1204            ++c, ++w) {
1205         if (*w == 0 && w + 1 != widths.end() && c + 1 == cE && !c->empty() &&
1206             c->at(0).length() > 1) {
1207           r->push_back(*c);
1208           break;
1209         }
1210       }
1211     }
1212     // output the formatted table
1213     char row_separator = '-';
1214     if (DEBUG)
1215       tgt << "\n..\n  TABLE WIDTH " << table_width << '\n';
1216     tgt << '\n';
1217     for (std::vector<table_row_type>::const_iterator r = table.begin(),
1218                                                      rE = table.end();
1219          r != rE; ++r) {
1220       if (r->size() == 1 && (r->at(0)[0] == "=" || r->at(0)[0] == "_")) {
1221         if (r->at(0)[0] == "=") {
1222           row_separator = '=';
1223         }
1224         continue;
1225       }
1226       for (std::vector<std::string::size_type>::const_iterator
1227                it = widths.begin(),
1228                E = widths.end();
1229            it != E; ++it) {
1230         if (*it > 0) {
1231           tgt << "+" << std::string(*it, row_separator);
1232         }
1233       }
1234       row_separator = '-'; // reset separator regardless of its value.
1235       tgt << "+\n";
1236       cell_type::size_type lines = 1;
1237       for (table_row_type::const_iterator c = r->begin(), cE = r->end();
1238            c != cE; ++c) {
1239         if (c->size() > lines) {
1240           lines = c->size();
1241         }
1242       }
1243       for (cell_type::size_type line = 0; line < lines; ++line) {
1244         std::vector<std::string::size_type>::const_iterator w = widths.begin();
1245         std::string::size_type width_so_far = 0;
1246         for (table_row_type::const_iterator c = r->begin(), cE = r->end();
1247              c != cE; ++c, ++w) {
1248           if (*w == 0) {
1249             continue;
1250           }
1251           tgt << '|';
1252           if (line < c->size()) {
1253             tgt << c->at(line);
1254             width_so_far += c->at(line).length();
1255             if (c + 1 != cE) {
1256               tgt << std::string(*w - c->at(line).length(), ' ');
1257               width_so_far += *w - c->at(line).length() + 1;
1258             }
1259           } else {
1260             tgt << std::string(*w, ' ');
1261             width_so_far += *w + 1;
1262           }
1263         }
1264         if (width_so_far < table_width) {
1265           if (width_so_far == 0) {
1266             --width_so_far;
1267             tgt << '|';
1268           }
1269           tgt << std::string(table_width - width_so_far, ' ');
1270         }
1271         tgt << "|\n";
1272       }
1273     }
1274     for (std::vector<std::string::size_type>::const_iterator
1275              it = widths.begin(),
1276              E = widths.end();
1277          it != E; ++it) {
1278       if (*it > 0) {
1279         tgt << "+" << std::string(*it, '-');
1280       }
1281     }
1282     tgt << "+\n\n";
1283   }
1284 
1285   void
generateListItem(std::stringstream & src,std::stringstream & tgt)1286   generateListItem(std::stringstream &src, std::stringstream &tgt)
1287   {
1288     src.seekg(0);
1289     std::string s;
1290     std::getline(src, s);
1291     tgt << '\n';
1292     if (!s.empty()) {
1293       tgt << s;
1294     }
1295     tgt << '\n';
1296     while (std::getline(src, s)) {
1297       if (!s.empty()) {
1298         tgt << "   " << s;
1299       }
1300       tgt << '\n';
1301     }
1302   }
1303 
1304   void
generateHighlightedItem(const std::string & markup,std::stringstream & tgt)1305   generateHighlightedItem(const std::string &markup, std::stringstream &tgt)
1306   {
1307     if (tokens.size() > 1) {
1308       tgt << markup << tokens[1] << markup;
1309       for (std::vector<std::string>::const_iterator it = tokens.begin() + 2,
1310                                                     E = tokens.end();
1311            it != E; ++it) {
1312         tgt << *it;
1313         if (it + 1 != E) {
1314           tgt << ' ';
1315         }
1316       }
1317       tgt << '\n';
1318     }
1319   }
1320 };
1321 
1322 /**
1323    \class is the base class for the implementations of utility programs ilmtp,
1324    machar, symutil, and symini.  It provides the methods common to all utility
1325    derived classes, such as reading lines from an input file, splitting a line
1326    to tokens, identifying the type of an NROFF directive represented by a line,
1327    and reporting errors with corresponding line numbers in the error messages.
1328  */
1329 class UtilityApplication
1330 {
1331 
1332 protected:
1333   SphinxConverter sphinx;          ///< sphinx output generator
1334   std::vector<std::string> tokens; ///< array of tokens in the last line
1335   std::vector<std::string>::const_iterator token; ///< tokens array iterator
1336   NroffInStream ifs;                              ///< input file stream
1337   std::string line; ///< the current line read from ifs
1338 
1339   void
1340   printError(ErrorSeverity sev, const char *txt1, const char *txt2 = "")
1341   {
1342     ifs.printError(sev, txt1, txt2);
1343   }
1344 
1345   /**
1346      \brief Create a string that is a result of converting every
1347      character of the given string to the lower case.
1348      \param s is the input string to be converted.
1349      \return a converted copy of the input string \a s.
1350    */
1351   std::string
makeLower(const std::string & s)1352   makeLower(const std::string &s)
1353   {
1354     std::string result;
1355     for (auto c = s.begin(), E = s.end(); c != E; ++c) {
1356       result.push_back(isupper(*c) ? tolower(*c) : *c);
1357     }
1358     return result;
1359   }
1360 
1361   /**
1362      \brief Read a line from the input stream and tokenize the string.
1363      \param elt map of line type strings to numeric type codes.
1364      \param os a pointer to an output stream.
1365      \return the type of line if found one of the lines with type in
1366              elt, otherwise LT_EOF when the entire input stream is
1367              read.
1368      If os is not nullptr, output every line that is discarded to the
1369      stream pointed by os.
1370   */
1371   int
1372   getLineAndTokenize(NroffMap &elt, std::ostream *os = nullptr)
1373   {
1374     tokens.clear();
1375     int result = LT_EOF;
1376     while (getline(ifs, line)) {
1377       sphinx.process(line);
1378       if (int item = elt.match(line)) {
1379         // collect tokens skipping the nroff macro at the line's head
1380         // which is the 3 first characters
1381         for (std::string::const_iterator s = line.begin() + 3, B = line.begin(),
1382                                          E = line.end();
1383              s != E;) {
1384           for (; *s == ' '; ++s) {
1385           } // skip over initial spaces
1386           // 'e' will point to one character after the end of a token
1387           auto e = s != E ? s + 1 : E; // next after s or end of line
1388           // move to next pairing \" or space if didn't start with \"
1389           for (; e != E && *e != (*s == '"' ? '"' : ' '); ++e) {
1390           }
1391           if (s != E && *s == '"') {
1392             ++s;          // exclude quotes
1393             if (e == E) { // validate quotes are paired
1394               printError(SEVERE, "double quote missing from end of string");
1395             }
1396           }
1397           tokens.push_back(s == e ? std::string() : line.substr(s - B, e - s));
1398           s = e == E ? e : e + 1;
1399         }
1400         result = item;
1401         break;
1402       }
1403       if (os) {
1404         *os << line << '\n';
1405       }
1406     }
1407     token = tokens.begin();
1408     return result;
1409   }
1410 
1411   /**
1412      \brief return the token currently pointed to by the token
1413      iterator and advance the iterator to the next token.  If no more
1414      tokens in the sequence, return an empty string.
1415    */
1416   std::string
getToken()1417   getToken()
1418   {
1419     return token != tokens.end() ? *token++ : std::string();
1420   }
1421 
1422   /**
1423      \brief output the comment asking not to modify the file produced
1424      by an utility application.
1425    */
1426   void
outputNotModifyComment(FILE * out,const std::string & filename,const std::string & progname,bool is_nroff)1427   outputNotModifyComment(FILE *out, const std::string &filename,
1428                          const std::string &progname, bool is_nroff)
1429   {
1430     auto pos = progname.find_last_of('/');
1431     std::string s =
1432         (pos == std::string::npos) ? progname : progname.substr(pos + 1);
1433     fputs(is_nroff ? ".\\\" " : "/* ", out);
1434     fprintf(out,
1435             "%s - This file written by utility program %s.  Do not modify.",
1436             filename.c_str(), s.c_str());
1437     fputs(is_nroff ? "\n" : " */\n", out);
1438   }
1439 
1440   /**
1441      \brief output the comment asking not to modify the file produced
1442      by an utility application.
1443    */
1444   void
outputNotModifyComment(std::ostream * out,const std::string & filename,const std::string & progname,bool is_nroff)1445   outputNotModifyComment(std::ostream *out, const std::string &filename,
1446                          const std::string &progname, bool is_nroff)
1447   {
1448     auto pos = progname.find_last_of('/');
1449     std::string s =
1450         (pos == std::string::npos) ? progname : progname.substr(pos + 1);
1451     if (is_nroff)
1452       *out << ".\\\" ";
1453     else
1454       *out << "/* ";
1455     *out << filename << " - This file written by utility program " << s
1456          << ".  Do not modify.";
1457     if (is_nroff)
1458       *out << "\n";
1459     else
1460       *out << " */\n";
1461   }
1462 };
1463 
1464 #else
1465 
1466 BEGIN_DECL_WITH_C_LINKAGE
1467 
1468 #define MAXLINE 300
1469 #define FUNCTION
1470 #define SUBROUTINE void
1471 #define LOOP while (1)
1472 #define INT int
1473 #define UINT unsigned int
1474 #define UCHAR char
1475 
1476 #define LT_EOF 0
1477 #define LT_UNK -1
1478 #ifndef TRUE
1479 #define TRUE 1
1480 #define FALSE 0
1481 #endif
1482 
1483 typedef struct {
1484   const char *str;
1485   char ltnum;
1486 } LT;
1487 
1488 extern FILE *infile[10], *outfile[10];
1489 
1490 #define IN1 infile[0]
1491 #define IN2 infile[1]
1492 #define IN3 infile[2]
1493 #define IN4 infile[3]
1494 
1495 #define OUT1 outfile[0]
1496 #define OUT2 outfile[1]
1497 #define OUT3 outfile[2]
1498 #define OUT4 outfile[3]
1499 #define OUT5 outfile[4]
1500 
1501 #define put_error put_err
1502 
1503 void put_error(int sev, const char *txt);
1504 void open_files(int argc, char **argv);
1505 int get_line(FILE *funit, LT ltypes[]);
1506 void get_token(char out[], int *len);
1507 int get_line1(FILE *funit, LT ltypes[], FILE *outf);
1508 void flush_line(FILE *outf);
1509 void output_off(void);
1510 void output_on(void);
1511 void ili_op(void);
1512 void init_ili(LT *elt);
1513 int get_ili(char *ilin);
1514 
1515 END_DECL_WITH_C_LINKAGE
1516 
1517 #endif
1518 
1519 #endif // _UTILS_UTILS_H
1520 
1521 /*
1522  Local Variables:
1523  mode: c++
1524  End:
1525 */
1526