DocConverter.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. //
  2. // Copyright (c) 2008-2013 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "Context.h"
  23. #include "File.h"
  24. #include "FileSystem.h"
  25. #include "List.h"
  26. #include "ProcessUtils.h"
  27. #include "StringUtils.h"
  28. #include <cstdio>
  29. #include <cstring>
  30. #ifdef WIN32
  31. #include <windows.h>
  32. #endif
  33. #include "DebugNew.h"
  34. using namespace Urho3D;
  35. SharedPtr<Context> context_(new Context());
  36. SharedPtr<FileSystem> fileSystem_(new FileSystem(context_));
  37. String inDir_;
  38. String outDir_;
  39. String mainPageName_;
  40. Vector<String> pageNames_;
  41. int main(int argc, char** argv);
  42. void Run(const Vector<String>& arguments);
  43. void ScanPageNames(const String& fileName);
  44. void ProcessFile(const String& fileName);
  45. String AssembleString(const Vector<String>& tokens, unsigned startIndex);
  46. void RemoveAutoLinks(String& line);
  47. bool IsPageName(const String& str, unsigned startIndex, unsigned endIndex);
  48. bool IsUpperCamelCase(const String& str, unsigned startIndex, unsigned endIndex);
  49. #define OUTPUTLINE(line) { if (outputFile.IsOpen()) outputFile.WriteLine(line); }
  50. int main(int argc, char** argv)
  51. {
  52. Vector<String> arguments;
  53. #ifdef WIN32
  54. arguments = ParseArguments(GetCommandLineW());
  55. #else
  56. arguments = ParseArguments(argc, argv);
  57. #endif
  58. Run(arguments);
  59. return EXIT_SUCCESS;
  60. }
  61. void Run(const Vector<String>& arguments)
  62. {
  63. if (arguments.Size() < 3)
  64. {
  65. ErrorExit(
  66. "Usage: DocConverter <dox input path> <wiki output path> <mainpage name>"
  67. );
  68. }
  69. inDir_ = AddTrailingSlash(arguments[0]);
  70. outDir_ = AddTrailingSlash(arguments[1]);
  71. mainPageName_ = arguments[2];
  72. if (!fileSystem_->DirExists(outDir_))
  73. ErrorExit("Wiki output path does not exist, conversion was skipped.");
  74. Vector<String> docFiles;
  75. fileSystem_->ScanDir(docFiles, inDir_, "*.dox", SCAN_FILES, false);
  76. pageNames_.Push(mainPageName_);
  77. for (unsigned i = 0; i < docFiles.Size(); ++i)
  78. ScanPageNames(inDir_ + docFiles[i]);
  79. for (unsigned i = 0; i < docFiles.Size(); ++i)
  80. ProcessFile(inDir_ + docFiles[i]);
  81. }
  82. void ScanPageNames(const String& fileName)
  83. {
  84. PrintLine("Scanning document file " + fileName + " for page names");
  85. File inputFile(context_, fileName);
  86. String outputFileName;
  87. File outputFile(context_);
  88. if (!inputFile.IsOpen())
  89. {
  90. PrintLine("WARNING: Failed to open input file " + fileName + ", skipping");
  91. return;
  92. }
  93. while (!inputFile.IsEof())
  94. {
  95. String line = inputFile.ReadLine();
  96. Vector<String> tokens = line.Split(' ');
  97. if (line.StartsWith("\\page") && tokens.Size() > 1)
  98. pageNames_.Push(tokens[1]);
  99. }
  100. }
  101. void ProcessFile(const String& fileName)
  102. {
  103. PrintLine("Processing document file " + fileName);
  104. File inputFile(context_, fileName);
  105. String outputFileName;
  106. File outputFile(context_);
  107. bool inVerbatim = false;
  108. if (!inputFile.IsOpen())
  109. {
  110. PrintLine("WARNING: Failed to open input file " + fileName + ", skipping");
  111. return;
  112. }
  113. while (!inputFile.IsEof())
  114. {
  115. String line = inputFile.ReadLine();
  116. if (!inVerbatim)
  117. {
  118. if (line.StartsWith("{") || line.StartsWith("}") || line.StartsWith("namespace") || line.StartsWith("/*") ||
  119. line.StartsWith("*/"))
  120. continue;
  121. line.Replace("%", "");
  122. // Handle escapes
  123. line.Replace("*", "\x060*\x060");
  124. if (line.Find("http") == String::NPOS)
  125. line.Replace("_", "\x060_\x060");
  126. line.Replace("[", "\x060[\x060");
  127. line.Replace("]", "\x060]\x060");
  128. line.Replace("&auml;", "\x0c3\x0a4");
  129. line.Replace("&ouml;", "\x0c3\x0b6");
  130. line.Replace("&Auml;", "\x0c3\x084");
  131. line.Replace("&Ouml;", "\x0c3\x096");
  132. line.Replace("\\n\\n", "<br>");
  133. line.Replace("\\n", "<br>");
  134. line.Replace("\\@", "@");
  135. // Handle tables
  136. if (line.StartsWith("|---"))
  137. continue;
  138. line.Replace("|", "||");
  139. // Replace links
  140. for (;;)
  141. {
  142. unsigned refIndex = line.Find("\\ref");
  143. if (refIndex != String::NPOS)
  144. {
  145. Vector<String> refTokens = line.Substring(refIndex).Split(' ');
  146. if (refTokens.Size() > 1)
  147. {
  148. String refTarget = refTokens[1];
  149. unsigned refTargetBegin = refIndex + 5;
  150. unsigned refTargetEnd = line.Find(' ', refTargetBegin + refTarget.Length());
  151. if (refTarget.EndsWith("."))
  152. {
  153. refTarget = refTarget.Substring(0, refTarget.Length() - 1);
  154. refTargetEnd--;
  155. }
  156. unsigned refBeginQuote = line.Find('\"', refTargetEnd);
  157. unsigned refEndQuote = line.Find('\"', refBeginQuote+1);
  158. if (IsPageName(refTarget, 0, refTarget.Length()))
  159. {
  160. if (refBeginQuote != String::NPOS && refEndQuote != String::NPOS)
  161. line = line.Substring(0, refIndex) + "[" + refTarget + " " + line.Substring(refBeginQuote + 1,
  162. refEndQuote - refBeginQuote - 1) + "]" + line.Substring(refEndQuote + 1);
  163. else
  164. line = line.Substring(0, refIndex) + "[" + refTarget + "]" + line.Substring(refTargetEnd);
  165. }
  166. else
  167. {
  168. // If link is not a valid page name, just output the link body as is
  169. if (refBeginQuote != String::NPOS && refEndQuote != String::NPOS)
  170. {
  171. line = line.Substring(0, refIndex) + line.Substring(refBeginQuote + 1,
  172. refEndQuote - refBeginQuote - 1) + line.Substring(refEndQuote + 1);
  173. }
  174. else
  175. line = line.Substring(0, refIndex) + line.Substring(refTargetBegin);
  176. }
  177. }
  178. else
  179. {
  180. PrintLine("WARNING: \\ref tag which could not be handled on line " + line);
  181. break;
  182. }
  183. }
  184. else
  185. break;
  186. }
  187. // Remove automatic wiki link generation from words that are not actual page names
  188. RemoveAutoLinks(line);
  189. Vector<String> tokens = line.Split(' ');
  190. // Check page and section transitions and handle markup conversion
  191. if (line.StartsWith("\\mainpage") && tokens.Size() > 1)
  192. {
  193. outputFileName = outDir_ + mainPageName_ + ".wiki";
  194. if (!outputFile.Open(outputFileName, FILE_WRITE))
  195. PrintLine("WARNING: Failed to open output file " + outputFileName);
  196. OUTPUTLINE("#labels featured")
  197. OUTPUTLINE("= " + AssembleString(tokens, 1) + " =")
  198. }
  199. else if (line.StartsWith("\\page") && tokens.Size() > 1)
  200. {
  201. outputFileName = outDir_ + tokens[1] + ".wiki";
  202. if (!outputFile.Open(outputFileName, FILE_WRITE))
  203. PrintLine("WARNING: Failed to open output file " + outputFileName);
  204. if (tokens.Size() > 2)
  205. OUTPUTLINE("= " + AssembleString(tokens, 2) + " =")
  206. else
  207. OUTPUTLINE("= " + tokens[1] + " =")
  208. }
  209. else if (line.StartsWith("\\section"))
  210. {
  211. if (tokens.Size() > 2)
  212. OUTPUTLINE("== " + AssembleString(tokens, 2) + " ==")
  213. else
  214. OUTPUTLINE("== " + tokens[1] + " ==")
  215. }
  216. else if (line.StartsWith("\\verbatim") || line.StartsWith("\\code"))
  217. {
  218. OUTPUTLINE("{{{")
  219. inVerbatim = true;
  220. }
  221. else if (line.StartsWith("- "))
  222. OUTPUTLINE(" * " + line.Substring(2))
  223. else if (line.StartsWith(" - "))
  224. OUTPUTLINE(" * " + line.Substring(4))
  225. else if (line.StartsWith("-# "))
  226. OUTPUTLINE(" * " + line.Substring(3))
  227. else
  228. OUTPUTLINE(line)
  229. }
  230. else
  231. {
  232. if (line.StartsWith("\\endverbatim") || line.StartsWith("\\endcode"))
  233. {
  234. outputFile.WriteLine("}}}");
  235. inVerbatim = false;
  236. }
  237. else
  238. outputFile.WriteLine(line);
  239. }
  240. }
  241. }
  242. String AssembleString(const Vector<String>& tokens, unsigned startIndex)
  243. {
  244. String ret;
  245. for (unsigned i = startIndex; i < tokens.Size(); ++i)
  246. {
  247. if (i > startIndex)
  248. ret += ' ';
  249. ret += tokens[i];
  250. }
  251. return ret;
  252. }
  253. void RemoveAutoLinks(String& line)
  254. {
  255. bool inLink = false;
  256. bool inWord = false;
  257. bool inWebLink = false;
  258. unsigned wordStart = 0;
  259. for (unsigned i = 0; i < line.Length(); ++i)
  260. {
  261. if (line[i] == '[')
  262. {
  263. inLink = true;
  264. continue;
  265. }
  266. else if (line[i] == ']')
  267. {
  268. inLink = false;
  269. continue;
  270. }
  271. if (line.Substring(i, 4) == "http")
  272. {
  273. inWebLink = true;
  274. i += 3;
  275. continue;
  276. }
  277. else if (inWebLink && !IsAlpha(line[i]) && !IsDigit(line[i]) && line[i] != '/' && line[i] != ':' && line[i] != '-' &&
  278. line[i] != '_' && line[i] != '.')
  279. inWebLink = false;
  280. else if (!inLink && !inWebLink)
  281. {
  282. if (!inWord && IsAlpha(line[i]))
  283. {
  284. inWord = true;
  285. wordStart = i;
  286. }
  287. else if (inWord && !IsAlpha(line[i]) && !IsDigit(line[i]))
  288. {
  289. inWord = false;
  290. unsigned wordEnd = i;
  291. if (!IsPageName(line, wordStart, wordEnd) && IsUpperCamelCase(line, wordStart, wordEnd))
  292. {
  293. line.Insert(wordStart, '!');
  294. ++i;
  295. }
  296. }
  297. }
  298. }
  299. if (inWord && !inLink && !inWebLink && !IsPageName(line, wordStart, line.Length()) && IsUpperCamelCase(line,
  300. wordStart, line.Length()))
  301. {
  302. line.Insert(wordStart, '!');
  303. }
  304. }
  305. bool IsPageName(const String& str, unsigned startIndex, unsigned endIndex)
  306. {
  307. String word = str.Substring(startIndex, endIndex - startIndex);
  308. for (unsigned i = 0; i < pageNames_.Size(); ++i)
  309. {
  310. if (word == pageNames_[i])
  311. return true;
  312. }
  313. return false;
  314. }
  315. bool IsUpperCamelCase(const String& str, unsigned startIndex, unsigned endIndex)
  316. {
  317. if (endIndex - startIndex < 2)
  318. return false;
  319. if (!isupper(str[startIndex]))
  320. return false;
  321. if (!islower(str[startIndex + 1]))
  322. return false;
  323. unsigned transitions = 1;
  324. for (unsigned i = startIndex + 2; i < endIndex - 1; ++i)
  325. {
  326. if (isupper(str[i]) && isupper(str[i+1]))
  327. return false;
  328. if (isupper(str[i]) && (islower(str[i+1]) || isdigit(str[i+1])))
  329. {
  330. ++transitions;
  331. ++i;
  332. }
  333. }
  334. return transitions > 1;
  335. }