DocConverter.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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", "<br>");
  133. // Replace links
  134. for (;;)
  135. {
  136. unsigned refIndex = line.Find("\\ref");
  137. if (refIndex != String::NPOS)
  138. {
  139. Vector<String> refTokens = line.Substring(refIndex).Split(' ');
  140. if (refTokens.Size() > 1)
  141. {
  142. String refTarget = refTokens[1];
  143. unsigned refTargetBegin = refIndex + 5;
  144. unsigned refTargetEnd = line.Find(' ', refTargetBegin + refTarget.Length());
  145. if (refTarget.EndsWith("."))
  146. {
  147. refTarget = refTarget.Substring(0, refTarget.Length() - 1);
  148. refTargetEnd--;
  149. }
  150. unsigned refBeginQuote = line.Find('\"', refTargetEnd);
  151. unsigned refEndQuote = line.Find('\"', refBeginQuote+1);
  152. if (IsPageName(refTarget, 0, refTarget.Length()))
  153. {
  154. if (refBeginQuote != String::NPOS && refEndQuote != String::NPOS)
  155. line = line.Substring(0, refIndex) + "[" + refTarget + " " + line.Substring(refBeginQuote + 1,
  156. refEndQuote - refBeginQuote - 1) + "]" + line.Substring(refEndQuote + 1);
  157. else
  158. line = line.Substring(0, refIndex) + "[" + refTarget + "]" + line.Substring(refTargetEnd);
  159. }
  160. else
  161. {
  162. // If link is not a valid page name, just output the link body as is
  163. if (refBeginQuote != String::NPOS && refEndQuote != String::NPOS)
  164. {
  165. line = line.Substring(0, refIndex) + line.Substring(refBeginQuote + 1,
  166. refEndQuote - refBeginQuote - 1) + line.Substring(refEndQuote + 1);
  167. }
  168. else
  169. line = line.Substring(0, refIndex) + line.Substring(refTargetBegin);
  170. }
  171. }
  172. else
  173. {
  174. PrintLine("WARNING: \\ref tag which could not be handled on line " + line);
  175. break;
  176. }
  177. }
  178. else
  179. break;
  180. }
  181. // Remove automatic wiki link generation from words that are not actual page names
  182. RemoveAutoLinks(line);
  183. Vector<String> tokens = line.Split(' ');
  184. // Check page and section transitions and handle markup conversion
  185. if (line.StartsWith("\\mainpage") && tokens.Size() > 1)
  186. {
  187. outputFileName = outDir_ + mainPageName_ + ".wiki";
  188. if (!outputFile.Open(outputFileName, FILE_WRITE))
  189. PrintLine("WARNING: Failed to open output file " + outputFileName);
  190. OUTPUTLINE("#labels featured")
  191. OUTPUTLINE("= " + AssembleString(tokens, 1) + " =")
  192. }
  193. else if (line.StartsWith("\\page") && tokens.Size() > 1)
  194. {
  195. outputFileName = outDir_ + tokens[1] + ".wiki";
  196. if (!outputFile.Open(outputFileName, FILE_WRITE))
  197. PrintLine("WARNING: Failed to open output file " + outputFileName);
  198. if (tokens.Size() > 2)
  199. OUTPUTLINE("= " + AssembleString(tokens, 2) + " =")
  200. else
  201. OUTPUTLINE("= " + tokens[1] + " =")
  202. }
  203. else if (line.StartsWith("\\section"))
  204. {
  205. if (tokens.Size() > 2)
  206. OUTPUTLINE("== " + AssembleString(tokens, 2) + " ==")
  207. else
  208. OUTPUTLINE("== " + tokens[1] + " ==")
  209. }
  210. else if (line.StartsWith("\\verbatim") || line.StartsWith("\\code"))
  211. {
  212. OUTPUTLINE("{{{")
  213. inVerbatim = true;
  214. }
  215. else if (line.StartsWith("- "))
  216. OUTPUTLINE(" * " + line.Substring(2))
  217. else if (line.StartsWith(" - "))
  218. OUTPUTLINE(" * " + line.Substring(4))
  219. else if (line.StartsWith("-# "))
  220. OUTPUTLINE(" * " + line.Substring(3))
  221. else
  222. OUTPUTLINE(line)
  223. }
  224. else
  225. {
  226. if (line.StartsWith("\\endverbatim") || line.StartsWith("\\endcode"))
  227. {
  228. outputFile.WriteLine("}}}");
  229. inVerbatim = false;
  230. }
  231. else
  232. outputFile.WriteLine(line);
  233. }
  234. }
  235. }
  236. String AssembleString(const Vector<String>& tokens, unsigned startIndex)
  237. {
  238. String ret;
  239. for (unsigned i = startIndex; i < tokens.Size(); ++i)
  240. {
  241. if (i > startIndex)
  242. ret += ' ';
  243. ret += tokens[i];
  244. }
  245. return ret;
  246. }
  247. void RemoveAutoLinks(String& line)
  248. {
  249. bool inLink = false;
  250. bool inWord = false;
  251. bool inWebLink = false;
  252. unsigned wordStart = 0;
  253. for (unsigned i = 0; i < line.Length(); ++i)
  254. {
  255. if (line[i] == '[')
  256. {
  257. inLink = true;
  258. continue;
  259. }
  260. else if (line[i] == ']')
  261. {
  262. inLink = false;
  263. continue;
  264. }
  265. if (line.Substring(i, 4) == "http")
  266. {
  267. inWebLink = true;
  268. i += 3;
  269. continue;
  270. }
  271. else if (inWebLink && !IsAlpha(line[i]) && !IsDigit(line[i]) && line[i] != '/' && line[i] != ':' && line[i] != '-' &&
  272. line[i] != '_' && line[i] != '.')
  273. inWebLink = false;
  274. else if (!inLink && !inWebLink)
  275. {
  276. if (!inWord && IsAlpha(line[i]))
  277. {
  278. inWord = true;
  279. wordStart = i;
  280. }
  281. else if (inWord && !IsAlpha(line[i]) && !IsDigit(line[i]))
  282. {
  283. inWord = false;
  284. unsigned wordEnd = i;
  285. if (!IsPageName(line, wordStart, wordEnd) && IsUpperCamelCase(line, wordStart, wordEnd))
  286. {
  287. line.Insert(wordStart, '!');
  288. ++i;
  289. }
  290. }
  291. }
  292. }
  293. if (inWord && !inLink && !inWebLink && !IsPageName(line, wordStart, line.Length()) && IsUpperCamelCase(line,
  294. wordStart, line.Length()))
  295. {
  296. line.Insert(wordStart, '!');
  297. }
  298. }
  299. bool IsPageName(const String& str, unsigned startIndex, unsigned endIndex)
  300. {
  301. String word = str.Substring(startIndex, endIndex - startIndex);
  302. for (unsigned i = 0; i < pageNames_.Size(); ++i)
  303. {
  304. if (word == pageNames_[i])
  305. return true;
  306. }
  307. return false;
  308. }
  309. bool IsUpperCamelCase(const String& str, unsigned startIndex, unsigned endIndex)
  310. {
  311. if (endIndex - startIndex < 2)
  312. return false;
  313. if (!isupper(str[startIndex]))
  314. return false;
  315. if (!islower(str[startIndex + 1]))
  316. return false;
  317. unsigned transitions = 1;
  318. for (unsigned i = startIndex + 2; i < endIndex - 1; ++i)
  319. {
  320. if (isupper(str[i]) && isupper(str[i+1]))
  321. return false;
  322. if (isupper(str[i]) && (islower(str[i+1]) || isdigit(str[i+1])))
  323. {
  324. ++transitions;
  325. ++i;
  326. }
  327. }
  328. return transitions > 1;
  329. }