1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/glue/nsINIParser.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,321 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// Moz headers (alphabetical) 1.10 +#include "nsCRTGlue.h" 1.11 +#include "nsError.h" 1.12 +#include "nsIFile.h" 1.13 +#include "nsINIParser.h" 1.14 +#include "mozilla/FileUtils.h" // AutoFILE 1.15 + 1.16 +// System headers (alphabetical) 1.17 +#include <stdio.h> 1.18 +#include <stdlib.h> 1.19 +#ifdef XP_WIN 1.20 +#include <windows.h> 1.21 +#endif 1.22 + 1.23 +#if defined(XP_WIN) 1.24 +#define READ_BINARYMODE L"rb" 1.25 +#else 1.26 +#define READ_BINARYMODE "r" 1.27 +#endif 1.28 + 1.29 +#ifdef XP_WIN 1.30 +inline FILE *TS_tfopen (const char *path, const wchar_t *mode) 1.31 +{ 1.32 + wchar_t wPath[MAX_PATH]; 1.33 + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); 1.34 + return _wfopen(wPath, mode); 1.35 +} 1.36 +#else 1.37 +inline FILE *TS_tfopen (const char *path, const char *mode) 1.38 +{ 1.39 + return fopen(path, mode); 1.40 +} 1.41 +#endif 1.42 + 1.43 +// Stack based FILE wrapper to ensure that fclose is called, copied from 1.44 +// toolkit/mozapps/update/updater/readstrings.cpp 1.45 + 1.46 +class AutoFILE { 1.47 +public: 1.48 + AutoFILE(FILE *fp = nullptr) : fp_(fp) {} 1.49 + ~AutoFILE() { if (fp_) fclose(fp_); } 1.50 + operator FILE *() { return fp_; } 1.51 + FILE** operator &() { return &fp_; } 1.52 + void operator=(FILE *fp) { fp_ = fp; } 1.53 +private: 1.54 + FILE *fp_; 1.55 +}; 1.56 + 1.57 +nsresult 1.58 +nsINIParser::Init(nsIFile* aFile) 1.59 +{ 1.60 + /* open the file. Don't use OpenANSIFileDesc, because you mustn't 1.61 + pass FILE* across shared library boundaries, which may be using 1.62 + different CRTs */ 1.63 + 1.64 + AutoFILE fd; 1.65 + 1.66 +#ifdef XP_WIN 1.67 + nsAutoString path; 1.68 + nsresult rv = aFile->GetPath(path); 1.69 + if (NS_WARN_IF(NS_FAILED(rv))) 1.70 + return rv; 1.71 + 1.72 + fd = _wfopen(path.get(), READ_BINARYMODE); 1.73 +#else 1.74 + nsAutoCString path; 1.75 + aFile->GetNativePath(path); 1.76 + 1.77 + fd = fopen(path.get(), READ_BINARYMODE); 1.78 +#endif 1.79 + 1.80 + if (!fd) 1.81 + return NS_ERROR_FAILURE; 1.82 + 1.83 + return InitFromFILE(fd); 1.84 +} 1.85 + 1.86 +nsresult 1.87 +nsINIParser::Init(const char *aPath) 1.88 +{ 1.89 + /* open the file */ 1.90 + AutoFILE fd = TS_tfopen(aPath, READ_BINARYMODE); 1.91 + 1.92 + if (!fd) 1.93 + return NS_ERROR_FAILURE; 1.94 + 1.95 + return InitFromFILE(fd); 1.96 +} 1.97 + 1.98 +static const char kNL[] = "\r\n"; 1.99 +static const char kEquals[] = "="; 1.100 +static const char kWhitespace[] = " \t"; 1.101 +static const char kRBracket[] = "]"; 1.102 + 1.103 +nsresult 1.104 +nsINIParser::InitFromFILE(FILE *fd) 1.105 +{ 1.106 + /* get file size */ 1.107 + if (fseek(fd, 0, SEEK_END) != 0) 1.108 + return NS_ERROR_FAILURE; 1.109 + 1.110 + long flen = ftell(fd); 1.111 + if (flen == 0) 1.112 + return NS_ERROR_FAILURE; 1.113 + 1.114 + /* malloc an internal buf the size of the file */ 1.115 + mFileContents = new char[flen + 2]; 1.116 + if (!mFileContents) 1.117 + return NS_ERROR_OUT_OF_MEMORY; 1.118 + 1.119 + /* read the file in one swoop */ 1.120 + if (fseek(fd, 0, SEEK_SET) != 0) 1.121 + return NS_BASE_STREAM_OSERROR; 1.122 + 1.123 + int rd = fread(mFileContents, sizeof(char), flen, fd); 1.124 + if (rd != flen) 1.125 + return NS_BASE_STREAM_OSERROR; 1.126 + 1.127 + // We write a UTF16 null so that the file is easier to convert to UTF8 1.128 + mFileContents[flen] = mFileContents[flen + 1] = '\0'; 1.129 + 1.130 + char *buffer = &mFileContents[0]; 1.131 + 1.132 + if (flen >= 3 1.133 + && mFileContents[0] == static_cast<char>(0xEF) 1.134 + && mFileContents[1] == static_cast<char>(0xBB) 1.135 + && mFileContents[2] == static_cast<char>(0xBF)) { 1.136 + // Someone set us up the Utf-8 BOM 1.137 + // This case is easy, since we assume that BOM-less 1.138 + // files are Utf-8 anyway. Just skip the BOM and process as usual. 1.139 + buffer = &mFileContents[3]; 1.140 + } 1.141 + 1.142 +#ifdef XP_WIN 1.143 + if (flen >= 2 1.144 + && mFileContents[0] == static_cast<char>(0xFF) 1.145 + && mFileContents[1] == static_cast<char>(0xFE)) { 1.146 + // Someone set us up the Utf-16LE BOM 1.147 + buffer = &mFileContents[2]; 1.148 + // Get the size required for our Utf8 buffer 1.149 + flen = WideCharToMultiByte(CP_UTF8, 1.150 + 0, 1.151 + reinterpret_cast<LPWSTR>(buffer), 1.152 + -1, 1.153 + nullptr, 1.154 + 0, 1.155 + nullptr, 1.156 + nullptr); 1.157 + if (0 == flen) { 1.158 + return NS_ERROR_FAILURE; 1.159 + } 1.160 + 1.161 + nsAutoArrayPtr<char> utf8Buffer(new char[flen]); 1.162 + if (0 == WideCharToMultiByte(CP_UTF8, 1.163 + 0, 1.164 + reinterpret_cast<LPWSTR>(buffer), 1.165 + -1, 1.166 + utf8Buffer, 1.167 + flen, 1.168 + nullptr, 1.169 + nullptr)) { 1.170 + return NS_ERROR_FAILURE; 1.171 + } 1.172 + mFileContents = utf8Buffer.forget(); 1.173 + buffer = mFileContents; 1.174 + } 1.175 +#endif 1.176 + 1.177 + char *currSection = nullptr; 1.178 + 1.179 + // outer loop tokenizes into lines 1.180 + while (char *token = NS_strtok(kNL, &buffer)) { 1.181 + if (token[0] == '#' || token[0] == ';') // it's a comment 1.182 + continue; 1.183 + 1.184 + token = (char*) NS_strspnp(kWhitespace, token); 1.185 + if (!*token) // empty line 1.186 + continue; 1.187 + 1.188 + if (token[0] == '[') { // section header! 1.189 + ++token; 1.190 + currSection = token; 1.191 + 1.192 + char *rb = NS_strtok(kRBracket, &token); 1.193 + if (!rb || NS_strtok(kWhitespace, &token)) { 1.194 + // there's either an unclosed [Section or a [Section]Moretext! 1.195 + // we could frankly decide that this INI file is malformed right 1.196 + // here and stop, but we won't... keep going, looking for 1.197 + // a well-formed [section] to continue working with 1.198 + currSection = nullptr; 1.199 + } 1.200 + 1.201 + continue; 1.202 + } 1.203 + 1.204 + if (!currSection) { 1.205 + // If we haven't found a section header (or we found a malformed 1.206 + // section header), don't bother parsing this line. 1.207 + continue; 1.208 + } 1.209 + 1.210 + char *key = token; 1.211 + char *e = NS_strtok(kEquals, &token); 1.212 + if (!e || !token) 1.213 + continue; 1.214 + 1.215 + INIValue *v; 1.216 + if (!mSections.Get(currSection, &v)) { 1.217 + v = new INIValue(key, token); 1.218 + if (!v) 1.219 + return NS_ERROR_OUT_OF_MEMORY; 1.220 + 1.221 + mSections.Put(currSection, v); 1.222 + continue; 1.223 + } 1.224 + 1.225 + // Check whether this key has already been specified; overwrite 1.226 + // if so, or append if not. 1.227 + while (v) { 1.228 + if (!strcmp(key, v->key)) { 1.229 + v->value = token; 1.230 + break; 1.231 + } 1.232 + if (!v->next) { 1.233 + v->next = new INIValue(key, token); 1.234 + if (!v->next) 1.235 + return NS_ERROR_OUT_OF_MEMORY; 1.236 + break; 1.237 + } 1.238 + v = v->next; 1.239 + } 1.240 + NS_ASSERTION(v, "v should never be null coming out of this loop"); 1.241 + } 1.242 + 1.243 + return NS_OK; 1.244 +} 1.245 + 1.246 +nsresult 1.247 +nsINIParser::GetString(const char *aSection, const char *aKey, 1.248 + nsACString &aResult) 1.249 +{ 1.250 + INIValue *val; 1.251 + mSections.Get(aSection, &val); 1.252 + 1.253 + while (val) { 1.254 + if (strcmp(val->key, aKey) == 0) { 1.255 + aResult.Assign(val->value); 1.256 + return NS_OK; 1.257 + } 1.258 + 1.259 + val = val->next; 1.260 + } 1.261 + 1.262 + return NS_ERROR_FAILURE; 1.263 +} 1.264 + 1.265 +nsresult 1.266 +nsINIParser::GetString(const char *aSection, const char *aKey, 1.267 + char *aResult, uint32_t aResultLen) 1.268 +{ 1.269 + INIValue *val; 1.270 + mSections.Get(aSection, &val); 1.271 + 1.272 + while (val) { 1.273 + if (strcmp(val->key, aKey) == 0) { 1.274 + strncpy(aResult, val->value, aResultLen); 1.275 + aResult[aResultLen - 1] = '\0'; 1.276 + if (strlen(val->value) >= aResultLen) 1.277 + return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; 1.278 + 1.279 + return NS_OK; 1.280 + } 1.281 + 1.282 + val = val->next; 1.283 + } 1.284 + 1.285 + return NS_ERROR_FAILURE; 1.286 +} 1.287 + 1.288 +PLDHashOperator 1.289 +nsINIParser::GetSectionsCB(const char *aKey, INIValue *aData, 1.290 + void *aClosure) 1.291 +{ 1.292 + GSClosureStruct *cs = reinterpret_cast<GSClosureStruct*>(aClosure); 1.293 + 1.294 + return cs->usercb(aKey, cs->userclosure) ? PL_DHASH_NEXT : PL_DHASH_STOP; 1.295 +} 1.296 + 1.297 +nsresult 1.298 +nsINIParser::GetSections(INISectionCallback aCB, void *aClosure) 1.299 +{ 1.300 + GSClosureStruct gs = { 1.301 + aCB, 1.302 + aClosure 1.303 + }; 1.304 + 1.305 + mSections.EnumerateRead(GetSectionsCB, &gs); 1.306 + return NS_OK; 1.307 +} 1.308 + 1.309 +nsresult 1.310 +nsINIParser::GetStrings(const char *aSection, 1.311 + INIStringCallback aCB, void *aClosure) 1.312 +{ 1.313 + INIValue *val; 1.314 + 1.315 + for (mSections.Get(aSection, &val); 1.316 + val; 1.317 + val = val->next) { 1.318 + 1.319 + if (!aCB(val->key, val->value, aClosure)) 1.320 + return NS_OK; 1.321 + } 1.322 + 1.323 + return NS_OK; 1.324 +}