xpcom/glue/nsINIParser.cpp

changeset 0
6474c204b198
     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 +}

mercurial