Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

823 lines
21 KiB

  1. //
  2. // hdivide.cpp -- yet another header file divider
  3. //
  4. // 1998 Nov Hiro Yamamoto
  5. //
  6. #pragma warning(disable: 4786)
  7. #include <cstdio>
  8. #include <string>
  9. #include <cstdarg>
  10. #include <map>
  11. #include <vector>
  12. #include <cassert>
  13. #include <io.h>
  14. #define PROGNAME "hdivide"
  15. #define VERSION "1.0"
  16. extern "C" {
  17. extern int getopt(int argc, char** argv, const char* opts);
  18. extern int optind;
  19. }
  20. namespace opt {
  21. bool verbose;
  22. }
  23. namespace input {
  24. unsigned long length;
  25. int lineno = 1;
  26. std::string path;
  27. std::string strip(const std::string& fname)
  28. {
  29. std::string stripped;
  30. //
  31. // find the "path" part
  32. //
  33. int n = fname.rfind('\\');
  34. if (n < 0) {
  35. n = fname.rfind('/');
  36. }
  37. if (n < 0 && (n = fname.rfind(':')) < 0) {
  38. n = 0;
  39. }
  40. else {
  41. ++n;
  42. }
  43. // store the path
  44. path = fname.substr(0, n);
  45. // retrive the filename portion
  46. stripped = fname.substr(n, fname.length());
  47. return stripped;
  48. }
  49. }
  50. #ifndef ARRAY_SIZE
  51. #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
  52. #endif
  53. namespace id {
  54. const char all[] = "all";
  55. const char begin[] = "begin";
  56. const char end[] = "end";
  57. const char else_[] = "else";
  58. const int begin_size = ARRAY_SIZE(begin) - 1;
  59. const int end_size = ARRAY_SIZE(end) - 1;
  60. const int else_size = ARRAY_SIZE(else_) - 1;
  61. const char internal[] = "internal";
  62. const char public_[] = "public";
  63. const char null[] = "null";
  64. std::string privatefile;
  65. std::string publicfile;
  66. const char insert[] = "insert";
  67. const int insert_size = ARRAY_SIZE(insert) - 1;
  68. const char reference_start[] = "reference_start";
  69. const char reference_end[] = "reference_end";
  70. }
  71. #define MYFAILURE_OPENFILE (120)
  72. #define MYFAILURE_INVALID_FORMAT (121)
  73. using namespace std;
  74. //////////////////////////////////////////////////////////////////////////
  75. // usage
  76. //////////////////////////////////////////////////////////////////////////
  77. void usage()
  78. {
  79. fputs(PROGNAME ": version " VERSION "\n", stderr);
  80. fputs("usage: hdivide [-v] input-filename (no path name please)\n", stderr);
  81. }
  82. //////////////////////////////////////////////////////////////////////////
  83. // misc. helpers
  84. //////////////////////////////////////////////////////////////////////////
  85. inline void makeupper(string& str)
  86. {
  87. for (int i = 0; i < str.length(); ++i) {
  88. str[i] = (char)toupper(str[i]);
  89. }
  90. }
  91. inline void makelower(string& str)
  92. {
  93. for (int i = 0; i < str.length(); ++i) {
  94. str[i] = (char)tolower(str[i]);
  95. }
  96. }
  97. namespace msg {
  98. void __cdecl error(const char* fmt, ...)
  99. {
  100. va_list args;
  101. fputs(PROGNAME ": [error] ", stderr);
  102. va_start(args, fmt);
  103. vfprintf(stderr, fmt, args);
  104. va_end(args);
  105. putc('\n', stderr);
  106. }
  107. void __cdecl verbose(const char* fmt, ...)
  108. {
  109. if (!opt::verbose)
  110. return;
  111. va_list args;
  112. fputs(PROGNAME ": ", stderr);
  113. va_start(args, fmt);
  114. vfprintf(stderr, fmt, args);
  115. va_end(args);
  116. putc('\n', stderr);
  117. }
  118. }
  119. //////////////////////////////////////////////////////////////////////////
  120. // class Output
  121. //////////////////////////////////////////////////////////////////////////
  122. class Output;
  123. class Insertion {
  124. public:
  125. // somehow the default constructor is required for std::vector
  126. // on NT5 build environment, as of Nov 1998
  127. explicit Insertion() : m_insert(NULL), m_insertion_point(-1) { }
  128. explicit Insertion(Output* insert, int point)
  129. : m_insert(insert), m_insertion_point(point)
  130. {
  131. }
  132. public:
  133. Output* m_insert;
  134. int m_insertion_point;
  135. };
  136. class Reference {
  137. public:
  138. // somehow the default constructor is required for std::vector
  139. // on NT5 build environment, as of Nov 1998
  140. Reference() : m_start(-1), m_end(-1) { }
  141. explicit Reference(int start, int end)
  142. : m_start(start), m_end(end) { }
  143. public:
  144. int m_start;
  145. int m_end;
  146. };
  147. class Output {
  148. public:
  149. explicit Output(const string& name)
  150. : m_name(name),
  151. m_fname(input::path + name + ".x"),
  152. m_alive(true),
  153. m_insertion_finished(false),
  154. m_reference_start(-1)
  155. {
  156. msg::verbose("opening %s", m_fname.c_str());
  157. if ((m_fp = fopen(m_fname.c_str(), "wt")) == NULL) {
  158. msg::error("cannot open file %s", m_fname.c_str());
  159. throw MYFAILURE_OPENFILE;
  160. }
  161. if (m_tomem) {
  162. m_buffer.reserve(input::length);
  163. }
  164. }
  165. virtual ~Output();
  166. public:
  167. void setalive(bool alive)
  168. {
  169. m_alive = alive;
  170. }
  171. bool getalive()
  172. {
  173. return m_alive;
  174. }
  175. const string& getname()
  176. {
  177. return m_name;
  178. }
  179. void put(int c)
  180. {
  181. assert(m_fp);
  182. if (m_alive) {
  183. if (m_tomem) {
  184. m_buffer += (char)c;
  185. }
  186. else {
  187. putc(c, m_fp);
  188. }
  189. }
  190. }
  191. void puts(const char* s)
  192. {
  193. assert(m_fp);
  194. if (m_alive) {
  195. if (m_tomem) {
  196. m_buffer += s;
  197. }
  198. else {
  199. fputs(s, m_fp);
  200. }
  201. }
  202. }
  203. bool operator<(const Output* a)
  204. {
  205. return m_name < a->m_name;
  206. }
  207. void set_insertion_point(Output* insert);
  208. void set_reference_start();
  209. void set_reference_end();
  210. bool do_insertion();
  211. protected:
  212. FILE* m_fp;
  213. bool m_alive;
  214. static bool m_tomem;
  215. string m_name;
  216. string m_fname;
  217. string m_buffer;
  218. vector<Insertion> m_insertions;
  219. bool m_insertion_finished;
  220. vector<Reference> m_references;
  221. int m_reference_start;
  222. int m_reference_start_line;
  223. };
  224. bool Output::m_tomem = true;
  225. Output::~Output()
  226. {
  227. if (m_reference_start != -1) {
  228. msg::error("reference started at line %d is not closed in tag '%s'",
  229. m_reference_start_line, m_name.c_str());
  230. throw MYFAILURE_INVALID_FORMAT;
  231. }
  232. if (!m_buffer.empty()) {
  233. msg::verbose("flushing %s", m_fname.c_str());
  234. fputs(m_buffer.c_str(), m_fp);
  235. }
  236. if (m_fp) {
  237. fclose(m_fp);
  238. }
  239. }
  240. void Output::set_insertion_point(Output* insert)
  241. {
  242. assert(insert!= NULL);
  243. if (m_alive) {
  244. Insertion i(insert, m_buffer.length());
  245. m_insertions.push_back(i);
  246. }
  247. }
  248. void Output::set_reference_start()
  249. {
  250. if (m_alive) {
  251. if (m_reference_start != -1) {
  252. msg::error("line %d: invalid reference_start appeared in tag context '%s'", input::lineno, m_name.c_str());
  253. throw MYFAILURE_INVALID_FORMAT;
  254. }
  255. m_reference_start = m_buffer.length();
  256. m_reference_start_line = input::lineno;
  257. }
  258. }
  259. void Output::set_reference_end()
  260. {
  261. if (m_alive) {
  262. if (m_reference_start == -1) {
  263. msg::error("line %d: invalid reference_end appeared in tag context '%s'", input::lineno, m_name.c_str());
  264. throw MYFAILURE_INVALID_FORMAT;
  265. }
  266. Reference ref(m_reference_start, m_buffer.length());
  267. msg::verbose("%s reference_end: %d - %d", m_name.c_str(), ref.m_start, ref.m_end);
  268. m_reference_start = -1;
  269. m_references.push_back(ref);
  270. }
  271. }
  272. bool Output::do_insertion()
  273. {
  274. if (!m_tomem || m_insertion_finished)
  275. return true;
  276. // to avoid infinite recursion by errornous commands,
  277. // firstly declare we've finished this.
  278. m_insertion_finished = true;
  279. int upto = m_insertions.size();
  280. for (int i = 0; i < upto; ++i) {
  281. Insertion& ins = m_insertions[i];
  282. assert(&ins);
  283. if (ins.m_insert->m_references.size() == 0) {
  284. msg::error("reference area is not specified or incorrect for tag '%s'", ins.m_insert->m_name.c_str());
  285. return false;
  286. }
  287. if (!ins.m_insert->m_insertion_finished) {
  288. if (!ins.m_insert->do_insertion())
  289. return false;
  290. }
  291. Output* o = ins.m_insert;
  292. for (int l = 0; l < o->m_references.size(); ++l) {
  293. Reference& ref = o->m_references[l];
  294. int len = ref.m_end - ref.m_start;
  295. msg::verbose("%s [%d] inserting text at %d, %s(%d - %d)",
  296. m_name.c_str(), l,
  297. ins.m_insertion_point,
  298. o->m_name.c_str(), ref.m_start, ref.m_start + len);
  299. m_buffer.insert(ins.m_insertion_point,
  300. o->m_buffer, ref.m_start,
  301. len);
  302. // fixup my insertions
  303. int point = ins.m_insertion_point;
  304. for (int k = 0; k < m_insertions.size(); ++k) {
  305. if (m_insertions[k].m_insertion_point >= point) {
  306. m_insertions[k].m_insertion_point += len;
  307. msg::verbose("%s [%d] insertion point fixed from %d to %d",
  308. m_name.c_str(), k,
  309. m_insertions[k].m_insertion_point - len,
  310. m_insertions[k].m_insertion_point);
  311. }
  312. }
  313. // fixup my references
  314. for (k = 0; k < m_references.size(); ++k) {
  315. msg::verbose("%s m_reference[%d].m_start=%d, m_end=%d adding len=%d", m_name.c_str(),
  316. k,
  317. m_references[k].m_start, m_references[k].m_end,
  318. len);
  319. if (m_references[k].m_start > point) {
  320. m_references[k].m_start += len;
  321. }
  322. if (m_references[k].m_end > point) {
  323. m_references[k].m_end += len;
  324. msg::verbose("finally start=%d, end=%d", m_references[k].m_start, m_references[k].m_end);
  325. }
  326. }
  327. }
  328. }
  329. return true;
  330. }
  331. //////////////////////////////////////////////////////////////////////////
  332. // class Divider
  333. //
  334. // this class manages the map of Output and performs misc. operations
  335. //////////////////////////////////////////////////////////////////////////
  336. class Divider : public map<string, Output*>
  337. {
  338. public:
  339. virtual ~Divider()
  340. {
  341. // process insertions
  342. for (iterator i = begin(); i != end(); ++i) {
  343. if (!i->second->do_insertion())
  344. break;
  345. }
  346. // clear up
  347. for (i = begin(); i != end(); ++i) {
  348. delete i->second;
  349. }
  350. }
  351. //////////////////////////////////////////////////////////////////////////
  352. // printout
  353. //
  354. // printout the argument to outputs
  355. //////////////////////////////////////////////////////////////////////////
  356. void printout(int c)
  357. {
  358. for (iterator i = begin(); i != end(); ++i) {
  359. i->second->put(c);
  360. }
  361. }
  362. void printout(const char* s)
  363. {
  364. for (iterator i = begin(); i != end(); ++i) {
  365. i->second->puts(s);
  366. }
  367. }
  368. void process_line(string& line);
  369. protected:
  370. void extract_version(const string& name, string& symbol, string& version, bool allow_omission = false);
  371. void get_arg(const string& name, string& arg);
  372. void prepare_section(string& name);
  373. void process_divider(string& line);
  374. void set_alive(bool alive)
  375. {
  376. for (iterator i = begin(); i != end(); ++i) {
  377. i->second->setalive(alive);
  378. }
  379. }
  380. typedef map<string, bool> OutputState;
  381. void push_state(OutputState& state)
  382. {
  383. state.clear();
  384. for (iterator i = begin(); i != end(); ++i) {
  385. state[i->second->getname()] = i->second->getalive();
  386. }
  387. }
  388. void pop_state(OutputState& state)
  389. {
  390. for (OutputState::iterator i = state.begin(); i != state.end(); ++i) {
  391. assert((*this)[i->first] != NULL);
  392. (*this)[i->first]->setalive(i->second);
  393. }
  394. }
  395. protected:
  396. string m_last_symbol;
  397. string m_last_version;
  398. };
  399. void Divider::prepare_section(string& name)
  400. {
  401. // make it lower case
  402. makelower(name);
  403. if (name == id::internal) {
  404. name = id::privatefile;
  405. }
  406. else if (name == id::public_) {
  407. name = id::publicfile;
  408. }
  409. if (name != id::null && (*this)[name] == NULL) {
  410. (*this)[name] = new Output(name);
  411. }
  412. }
  413. //////////////////////////////////////////////////////////////////////////
  414. // Divider::extract_version
  415. //
  416. // extracts version symbol and supported version
  417. //
  418. // "begin_symbol_version" is splited to symbol and version.
  419. // Both are stored in upper case.
  420. //////////////////////////////////////////////////////////////////////////
  421. void Divider::extract_version(const string& name, string& symbol, string& version, bool allow_omission /*= false*/)
  422. {
  423. int nsymbol = name.find('_');
  424. int nver = name.rfind('_');
  425. if (nsymbol == -1 || nver == nsymbol) {
  426. if (allow_omission) {
  427. symbol = m_last_symbol;
  428. version = m_last_version;
  429. return;
  430. }
  431. else {
  432. msg::error("line %d: invalid version specifier '%s'", input::lineno, name.c_str());
  433. throw MYFAILURE_INVALID_FORMAT;
  434. }
  435. }
  436. // symbol
  437. symbol = name.substr(nsymbol + 1, nver - nsymbol - 1);
  438. // upper case
  439. makeupper(symbol);
  440. version = "0000" + name.substr(nver + 1, name.length());
  441. version = version.substr(version.length() - 4, 4);
  442. makeupper(version);
  443. m_last_symbol = symbol;
  444. m_last_version = version;
  445. }
  446. //////////////////////////////////////////////////////////////////////////
  447. // Divider::get_arg
  448. //
  449. // extracts one argument separated by "_"
  450. //////////////////////////////////////////////////////////////////////////
  451. void Divider::get_arg(const string& name, string& arg)
  452. {
  453. int npos = name.find('_');
  454. if (npos == -1) {
  455. msg::error("line %d: command incompleted in '%s'", input::lineno, name.c_str());
  456. throw MYFAILURE_INVALID_FORMAT;
  457. }
  458. arg = name.substr(npos + 1, name.length());
  459. }
  460. //////////////////////////////////////////////////////////////////////////
  461. // Divider::process_divider
  462. //
  463. // processes the divider instructions
  464. //////////////////////////////////////////////////////////////////////////
  465. void Divider::process_divider(string& line)
  466. {
  467. const char* p = line.begin();
  468. ++p;
  469. bool makelive = true;
  470. if (*p == '!') {
  471. makelive = false;
  472. ++p;
  473. }
  474. // skip the heading spaces
  475. while (isspace(*p))
  476. ++p;
  477. for (int col = 0; p != line.end(); ++col) {
  478. // pickup the name
  479. string name;
  480. while (*p != ';' && p != line.end()) {
  481. if (!isspace(*p)) {
  482. name += *p;
  483. }
  484. ++p;
  485. }
  486. if (p != line.end()) {
  487. ++p;
  488. }
  489. // first column may have special meaning
  490. if (col == 0) {
  491. if (name == id::all) {
  492. set_alive(makelive);
  493. // does "!all" make sense ?
  494. // however i'm supporting it anyway
  495. break;
  496. }
  497. if (name == id::null) {
  498. set_alive(!makelive);
  499. break;
  500. }
  501. if (name.substr(0, id::insert_size) == id::insert) {
  502. string insert;
  503. get_arg(name, insert);
  504. prepare_section(insert);
  505. if (insert == id::null || insert == id::all) {
  506. msg::error("line %d: invalid insertion of '%s'", input::lineno, insert.c_str());
  507. throw MYFAILURE_INVALID_FORMAT;
  508. }
  509. assert((*this)[insert] != NULL);
  510. for (iterator i = begin(); i != end(); ++i) {
  511. (*this)[i->first]->set_insertion_point((*this)[insert]);
  512. }
  513. break;
  514. }
  515. if (name == id::reference_start) {
  516. for (iterator i = begin(); i != end(); ++i) {
  517. (*this)[i->first]->set_reference_start();
  518. }
  519. break;
  520. }
  521. if (name == id::reference_end) {
  522. for (iterator i = begin(); i != end(); ++i) {
  523. (*this)[i->first]->set_reference_end();
  524. }
  525. break;
  526. }
  527. if (name.substr(0, id::begin_size) == id::begin) {
  528. string symbol;
  529. string version;
  530. extract_version(name, symbol, version);
  531. printout("#if (");
  532. printout(symbol.c_str());
  533. printout(" >= 0x");
  534. printout(version.c_str());
  535. printout(")\n");
  536. break;
  537. }
  538. if (name.substr(0, id::else_size) == id::else_) {
  539. printout("#else\n");
  540. break;
  541. }
  542. if (name.substr(0, id::end_size) == id::end) {
  543. string symbol;
  544. string version;
  545. extract_version(name, symbol, version, true);
  546. printout("#endif /* ");
  547. printout(symbol.c_str());
  548. printout(" >= 0x");
  549. printout(version.c_str());
  550. printout(" */\n");
  551. break;
  552. }
  553. // setup the initial state
  554. set_alive(!makelive);
  555. }
  556. prepare_section(name);
  557. (*this)[name]->setalive(makelive);
  558. }
  559. }
  560. //////////////////////////////////////////////////////////////////////////
  561. // Divider::process_line
  562. //
  563. // handles one line
  564. //////////////////////////////////////////////////////////////////////////
  565. void Divider::process_line(string& line)
  566. {
  567. if (line[0] == ';') {
  568. process_divider(line);
  569. }
  570. else {
  571. // check if inline section appears
  572. bool instr = false;
  573. const char* p = line.begin();
  574. const char* section = NULL;
  575. while (p != line.end()) {
  576. if (*p == '\\' && (p + 1) != line.end()) {
  577. // skip escape character
  578. // note: no consideration for Shift JIS
  579. ++p;
  580. }
  581. else if (*p == '"' || *p == '\'') {
  582. // beginning of end of literal
  583. instr = !instr;
  584. }
  585. else if (*p == '@' && !instr) {
  586. // we have inline section
  587. section = p;
  588. break;
  589. }
  590. ++p;
  591. }
  592. if (section) {
  593. //
  594. // if inline tag is specified, temporarily change
  595. // the output
  596. //
  597. OutputState state;
  598. push_state(state);
  599. assert(*p == '@');
  600. ++p;
  601. if (*p == '+') {
  602. ++p;
  603. }
  604. else {
  605. set_alive(false);
  606. }
  607. while (p != line.end()) {
  608. string name;
  609. while (*p != ';' && p != line.end()) {
  610. if (!isspace(*p)) {
  611. name += *p;
  612. }
  613. ++p;
  614. }
  615. if (p != line.end())
  616. ++p;
  617. if (name == id::all) {
  618. set_alive(true);
  619. break;
  620. }
  621. if (name == id::null) {
  622. set_alive(false);
  623. break;
  624. }
  625. prepare_section(name);
  626. (*this)[name]->setalive(true);
  627. }
  628. // trim trailing spaces
  629. int i = section - line.begin() - 1;
  630. while (i >= 0 && isspace(line[i])) {
  631. --i;
  632. }
  633. line = line.substr(0, i + 1);
  634. printout(line.c_str());
  635. printout('\n');
  636. pop_state(state);
  637. }
  638. else {
  639. printout(line.c_str());
  640. printout('\n');
  641. }
  642. }
  643. ++input::lineno;
  644. }
  645. //////////////////////////////////////////////////////////////////////////
  646. // hdivide
  647. //////////////////////////////////////////////////////////////////////////
  648. void hdivide(FILE* fp)
  649. {
  650. Divider divider;
  651. divider[id::publicfile] = new Output(id::publicfile);
  652. divider[id::privatefile] = new Output(id::privatefile);
  653. string line;
  654. int c;
  655. while ((c = getc(fp)) != EOF) {
  656. if (c == '\n') {
  657. divider.process_line(line);
  658. line = "";
  659. }
  660. else {
  661. line += (char)c;
  662. }
  663. }
  664. if (!line.empty())
  665. divider.process_line(line);
  666. }
  667. //////////////////////////////////////////////////////////////////////////
  668. // main
  669. //////////////////////////////////////////////////////////////////////////
  670. int __cdecl main(int argc, char** argv)
  671. {
  672. int c;
  673. while ((c = getopt(argc, argv, "v")) != EOF) {
  674. switch (c) {
  675. case 'v':
  676. opt::verbose = true;
  677. break;
  678. default:
  679. usage();
  680. return EXIT_FAILURE;
  681. }
  682. }
  683. if (optind == argc) {
  684. usage();
  685. return EXIT_FAILURE;
  686. }
  687. msg::verbose("input file: %s", argv[optind]);
  688. FILE* fp = fopen(argv[optind], "rt");
  689. if (fp == NULL) {
  690. msg::error("cannot open input file %s", argv[optind]);
  691. return EXIT_FAILURE;
  692. }
  693. input::length = _filelength(_fileno(fp));
  694. id::publicfile = argv[optind];
  695. id::publicfile = input::strip(id::publicfile.substr(0, id::publicfile.length() - 2));
  696. id::privatefile = id::publicfile + "p";
  697. int exitcode = EXIT_SUCCESS;
  698. try {
  699. hdivide(fp);
  700. } catch (int err) {
  701. exitcode = EXIT_FAILURE;
  702. switch (err) {
  703. case MYFAILURE_OPENFILE:
  704. break;
  705. case MYFAILURE_INVALID_FORMAT:
  706. msg::error("fatal: invalid format");
  707. break;
  708. }
  709. } catch (...) {
  710. exitcode = EXIT_FAILURE;
  711. }
  712. fclose(fp);
  713. return exitcode;
  714. }