nsINIParser.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. // Moz headers (alphabetical)
  6. #include "nsCRTGlue.h"
  7. #include "nsError.h"
  8. #include "nsIFile.h"
  9. #include "nsINIParser.h"
  10. #include "mozilla/FileUtils.h" // AutoFILE
  11. // System headers (alphabetical)
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #ifdef XP_WIN
  15. #include <windows.h>
  16. #endif
  17. #if defined(XP_WIN)
  18. #define READ_BINARYMODE L"rb"
  19. #else
  20. #define READ_BINARYMODE "r"
  21. #endif
  22. using namespace mozilla;
  23. #ifdef XP_WIN
  24. inline FILE*
  25. TS_tfopen(const char* aPath, const wchar_t* aMode)
  26. {
  27. wchar_t wPath[MAX_PATH];
  28. MultiByteToWideChar(CP_UTF8, 0, aPath, -1, wPath, MAX_PATH);
  29. return _wfopen(wPath, aMode);
  30. }
  31. #else
  32. inline FILE*
  33. TS_tfopen(const char* aPath, const char* aMode)
  34. {
  35. return fopen(aPath, aMode);
  36. }
  37. #endif
  38. // Stack based FILE wrapper to ensure that fclose is called, copied from
  39. // toolkit/mozapps/update/updater/readstrings.cpp
  40. class AutoFILE
  41. {
  42. public:
  43. explicit AutoFILE(FILE* aFp = nullptr) : fp_(aFp) {}
  44. ~AutoFILE()
  45. {
  46. if (fp_) {
  47. fclose(fp_);
  48. }
  49. }
  50. operator FILE*() { return fp_; }
  51. FILE** operator&() { return &fp_; }
  52. void operator=(FILE* aFp) { fp_ = aFp; }
  53. private:
  54. FILE* fp_;
  55. };
  56. nsresult
  57. nsINIParser::Init(nsIFile* aFile)
  58. {
  59. /* open the file. Don't use OpenANSIFileDesc, because you mustn't
  60. pass FILE* across shared library boundaries, which may be using
  61. different CRTs */
  62. AutoFILE fd;
  63. #ifdef XP_WIN
  64. nsAutoString path;
  65. nsresult rv = aFile->GetPath(path);
  66. if (NS_WARN_IF(NS_FAILED(rv))) {
  67. return rv;
  68. }
  69. fd = _wfopen(path.get(), READ_BINARYMODE);
  70. #else
  71. nsAutoCString path;
  72. aFile->GetNativePath(path);
  73. fd = fopen(path.get(), READ_BINARYMODE);
  74. #endif
  75. if (!fd) {
  76. return NS_ERROR_FAILURE;
  77. }
  78. return InitFromFILE(fd);
  79. }
  80. nsresult
  81. nsINIParser::Init(const char* aPath)
  82. {
  83. /* open the file */
  84. AutoFILE fd(TS_tfopen(aPath, READ_BINARYMODE));
  85. if (!fd) {
  86. return NS_ERROR_FAILURE;
  87. }
  88. return InitFromFILE(fd);
  89. }
  90. static const char kNL[] = "\r\n";
  91. static const char kEquals[] = "=";
  92. static const char kWhitespace[] = " \t";
  93. static const char kRBracket[] = "]";
  94. nsresult
  95. nsINIParser::InitFromFILE(FILE* aFd)
  96. {
  97. /* get file size */
  98. if (fseek(aFd, 0, SEEK_END) != 0) {
  99. return NS_ERROR_FAILURE;
  100. }
  101. long flen = ftell(aFd);
  102. /* zero-sized file, or an error */
  103. if (flen <= 0) {
  104. return NS_ERROR_FAILURE;
  105. }
  106. /* malloc an internal buf the size of the file */
  107. mFileContents = MakeUnique<char[]>(flen + 2);
  108. if (!mFileContents) {
  109. return NS_ERROR_OUT_OF_MEMORY;
  110. }
  111. /* read the file in one swoop */
  112. if (fseek(aFd, 0, SEEK_SET) != 0) {
  113. return NS_BASE_STREAM_OSERROR;
  114. }
  115. int rd = fread(mFileContents.get(), sizeof(char), flen, aFd);
  116. if (rd != flen) {
  117. return NS_BASE_STREAM_OSERROR;
  118. }
  119. // We write a UTF16 null so that the file is easier to convert to UTF8
  120. mFileContents[flen] = mFileContents[flen + 1] = '\0';
  121. char* buffer = &mFileContents[0];
  122. if (flen >= 3 &&
  123. mFileContents[0] == '\xEF' &&
  124. mFileContents[1] == '\xBB' &&
  125. mFileContents[2] == '\xBF') {
  126. // Someone set us up the Utf-8 BOM
  127. // This case is easy, since we assume that BOM-less
  128. // files are Utf-8 anyway. Just skip the BOM and process as usual.
  129. buffer = &mFileContents[3];
  130. }
  131. #ifdef XP_WIN
  132. if (flen >= 2 &&
  133. mFileContents[0] == '\xFF' &&
  134. mFileContents[1] == '\xFE') {
  135. // Someone set us up the Utf-16LE BOM
  136. buffer = &mFileContents[2];
  137. // Get the size required for our Utf8 buffer
  138. flen = WideCharToMultiByte(CP_UTF8,
  139. 0,
  140. reinterpret_cast<LPWSTR>(buffer),
  141. -1,
  142. nullptr,
  143. 0,
  144. nullptr,
  145. nullptr);
  146. if (flen == 0) {
  147. return NS_ERROR_FAILURE;
  148. }
  149. UniquePtr<char[]> utf8Buffer(new char[flen]);
  150. if (WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPWSTR>(buffer), -1,
  151. utf8Buffer.get(), flen, nullptr, nullptr) == 0) {
  152. return NS_ERROR_FAILURE;
  153. }
  154. mFileContents = Move(utf8Buffer);
  155. buffer = mFileContents.get();
  156. }
  157. #endif
  158. char* currSection = nullptr;
  159. // outer loop tokenizes into lines
  160. while (char* token = NS_strtok(kNL, &buffer)) {
  161. if (token[0] == '#' || token[0] == ';') { // it's a comment
  162. continue;
  163. }
  164. token = (char*)NS_strspnp(kWhitespace, token);
  165. if (!*token) { // empty line
  166. continue;
  167. }
  168. if (token[0] == '[') { // section header!
  169. ++token;
  170. currSection = token;
  171. char* rb = NS_strtok(kRBracket, &token);
  172. if (!rb || NS_strtok(kWhitespace, &token)) {
  173. // there's either an unclosed [Section or a [Section]Moretext!
  174. // we could frankly decide that this INI file is malformed right
  175. // here and stop, but we won't... keep going, looking for
  176. // a well-formed [section] to continue working with
  177. currSection = nullptr;
  178. }
  179. continue;
  180. }
  181. if (!currSection) {
  182. // If we haven't found a section header (or we found a malformed
  183. // section header), don't bother parsing this line.
  184. continue;
  185. }
  186. char* key = token;
  187. char* e = NS_strtok(kEquals, &token);
  188. if (!e || !token) {
  189. continue;
  190. }
  191. INIValue* v;
  192. if (!mSections.Get(currSection, &v)) {
  193. v = new INIValue(key, token);
  194. if (!v) {
  195. return NS_ERROR_OUT_OF_MEMORY;
  196. }
  197. mSections.Put(currSection, v);
  198. continue;
  199. }
  200. // Check whether this key has already been specified; overwrite
  201. // if so, or append if not.
  202. while (v) {
  203. if (!strcmp(key, v->key)) {
  204. v->value = token;
  205. break;
  206. }
  207. if (!v->next) {
  208. v->next = MakeUnique<INIValue>(key, token);
  209. if (!v->next) {
  210. return NS_ERROR_OUT_OF_MEMORY;
  211. }
  212. break;
  213. }
  214. v = v->next.get();
  215. }
  216. NS_ASSERTION(v, "v should never be null coming out of this loop");
  217. }
  218. return NS_OK;
  219. }
  220. nsresult
  221. nsINIParser::GetString(const char* aSection, const char* aKey,
  222. nsACString& aResult)
  223. {
  224. INIValue* val;
  225. mSections.Get(aSection, &val);
  226. while (val) {
  227. if (strcmp(val->key, aKey) == 0) {
  228. aResult.Assign(val->value);
  229. return NS_OK;
  230. }
  231. val = val->next.get();
  232. }
  233. return NS_ERROR_FAILURE;
  234. }
  235. nsresult
  236. nsINIParser::GetString(const char* aSection, const char* aKey,
  237. char* aResult, uint32_t aResultLen)
  238. {
  239. INIValue* val;
  240. mSections.Get(aSection, &val);
  241. while (val) {
  242. if (strcmp(val->key, aKey) == 0) {
  243. strncpy(aResult, val->value, aResultLen);
  244. aResult[aResultLen - 1] = '\0';
  245. if (strlen(val->value) >= aResultLen) {
  246. return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
  247. }
  248. return NS_OK;
  249. }
  250. val = val->next.get();
  251. }
  252. return NS_ERROR_FAILURE;
  253. }
  254. nsresult
  255. nsINIParser::GetSections(INISectionCallback aCB, void* aClosure)
  256. {
  257. for (auto iter = mSections.Iter(); !iter.Done(); iter.Next()) {
  258. if (!aCB(iter.Key(), aClosure)) {
  259. break;
  260. }
  261. }
  262. return NS_OK;
  263. }
  264. nsresult
  265. nsINIParser::GetStrings(const char* aSection,
  266. INIStringCallback aCB, void* aClosure)
  267. {
  268. INIValue* val;
  269. for (mSections.Get(aSection, &val);
  270. val;
  271. val = val->next.get()) {
  272. if (!aCB(val->key, val->value, aClosure)) {
  273. return NS_OK;
  274. }
  275. }
  276. return NS_OK;
  277. }