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