Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/base/nsINIParser.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
// Moz headers (alphabetical)
8
#include "nsCRTGlue.h"
9
#include "nsError.h"
10
#include "nsIFile.h"
11
#include "nsINIParser.h"
12
#include "mozilla/ResultExtensions.h"
13
#include "mozilla/URLPreloader.h"
14
15
using namespace mozilla;
16
17
nsresult
18
nsINIParser::Init(nsIFile* aFile)
19
0
{
20
0
  nsCString result;
21
0
  MOZ_TRY_VAR(result, URLPreloader::ReadFile(aFile));
22
0
23
0
  return InitFromString(result);
24
0
}
25
26
static const char kNL[] = "\r\n";
27
static const char kEquals[] = "=";
28
static const char kWhitespace[] = " \t";
29
static const char kRBracket[] = "]";
30
31
nsresult
32
nsINIParser::InitFromString(const nsCString& aStr)
33
0
{
34
0
  nsCString fileContents;
35
0
  char* buffer;
36
0
37
0
  if (StringHead(aStr, 3) == "\xEF\xBB\xBF") {
38
0
    // Someone set us up the Utf-8 BOM
39
0
    // This case is easy, since we assume that BOM-less
40
0
    // files are Utf-8 anyway.  Just skip the BOM and process as usual.
41
0
    fileContents.Append(aStr);
42
0
    buffer = fileContents.BeginWriting() + 3;
43
0
  } else {
44
0
    if (StringHead(aStr, 2) == "\xFF\xFE") {
45
0
      // Someone set us up the Utf-16LE BOM
46
0
      nsDependentSubstring str(reinterpret_cast<const char16_t*>(aStr.get()),
47
0
                               aStr.Length() / 2);
48
0
49
0
      AppendUTF16toUTF8(Substring(str, 1), fileContents);
50
0
    } else {
51
0
      fileContents.Append(aStr);
52
0
    }
53
0
54
0
    buffer = fileContents.BeginWriting();
55
0
  }
56
0
57
0
  char* currSection = nullptr;
58
0
59
0
  // outer loop tokenizes into lines
60
0
  while (char* token = NS_strtok(kNL, &buffer)) {
61
0
    if (token[0] == '#' || token[0] == ';') { // it's a comment
62
0
      continue;
63
0
    }
64
0
65
0
    token = (char*)NS_strspnp(kWhitespace, token);
66
0
    if (!*token) { // empty line
67
0
      continue;
68
0
    }
69
0
70
0
    if (token[0] == '[') { // section header!
71
0
      ++token;
72
0
      currSection = token;
73
0
74
0
      char* rb = NS_strtok(kRBracket, &token);
75
0
      if (!rb || NS_strtok(kWhitespace, &token)) {
76
0
        // there's either an unclosed [Section or a [Section]Moretext!
77
0
        // we could frankly decide that this INI file is malformed right
78
0
        // here and stop, but we won't... keep going, looking for
79
0
        // a well-formed [section] to continue working with
80
0
        currSection = nullptr;
81
0
      }
82
0
83
0
      continue;
84
0
    }
85
0
86
0
    if (!currSection) {
87
0
      // If we haven't found a section header (or we found a malformed
88
0
      // section header), don't bother parsing this line.
89
0
      continue;
90
0
    }
91
0
92
0
    char* key = token;
93
0
    char* e = NS_strtok(kEquals, &token);
94
0
    if (!e || !token) {
95
0
      continue;
96
0
    }
97
0
98
0
    SetString(currSection, key, token);
99
0
  }
100
0
101
0
  return NS_OK;
102
0
}
103
104
bool
105
nsINIParser::IsValidSection(const char* aSection)
106
0
{
107
0
  if (aSection[0] == '\0') {
108
0
    return false;
109
0
  }
110
0
111
0
  const char* found = strpbrk(aSection, "\r\n[]");
112
0
  return found == nullptr;
113
0
}
114
115
bool
116
nsINIParser::IsValidKey(const char* aKey)
117
0
{
118
0
  if (aKey[0] == '\0') {
119
0
    return false;
120
0
  }
121
0
122
0
  const char* found = strpbrk(aKey, "\r\n=");
123
0
  return found == nullptr;
124
0
}
125
126
bool
127
nsINIParser::IsValidValue(const char* aValue)
128
0
{
129
0
  const char* found = strpbrk(aValue, "\r\n");
130
0
  return found == nullptr;
131
0
}
132
133
nsresult
134
nsINIParser::GetString(const char* aSection, const char* aKey,
135
                       nsACString& aResult)
136
0
{
137
0
  if (!IsValidSection(aSection) || !IsValidKey(aKey)) {
138
0
    return NS_ERROR_INVALID_ARG;
139
0
  }
140
0
141
0
  INIValue* val;
142
0
  mSections.Get(aSection, &val);
143
0
144
0
  while (val) {
145
0
    if (strcmp(val->key, aKey) == 0) {
146
0
      aResult.Assign(val->value);
147
0
      return NS_OK;
148
0
    }
149
0
150
0
    val = val->next.get();
151
0
  }
152
0
153
0
  return NS_ERROR_FAILURE;
154
0
}
155
156
nsresult
157
nsINIParser::GetString(const char* aSection, const char* aKey,
158
                       char* aResult, uint32_t aResultLen)
159
0
{
160
0
  if (!IsValidSection(aSection) || !IsValidKey(aKey)) {
161
0
    return NS_ERROR_INVALID_ARG;
162
0
  }
163
0
164
0
  INIValue* val;
165
0
  mSections.Get(aSection, &val);
166
0
167
0
  while (val) {
168
0
    if (strcmp(val->key, aKey) == 0) {
169
0
      strncpy(aResult, val->value, aResultLen);
170
0
      aResult[aResultLen - 1] = '\0';
171
0
      if (strlen(val->value) >= aResultLen) {
172
0
        return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
173
0
      }
174
0
175
0
      return NS_OK;
176
0
    }
177
0
178
0
    val = val->next.get();
179
0
  }
180
0
181
0
  return NS_ERROR_FAILURE;
182
0
}
183
184
nsresult
185
nsINIParser::GetSections(INISectionCallback aCB, void* aClosure)
186
0
{
187
0
  for (auto iter = mSections.Iter(); !iter.Done(); iter.Next()) {
188
0
    if (!aCB(iter.Key(), aClosure)) {
189
0
      break;
190
0
    }
191
0
  }
192
0
  return NS_OK;
193
0
}
194
195
nsresult
196
nsINIParser::GetStrings(const char* aSection,
197
                        INIStringCallback aCB, void* aClosure)
198
0
{
199
0
  if (!IsValidSection(aSection)) {
200
0
    return NS_ERROR_INVALID_ARG;
201
0
  }
202
0
203
0
  INIValue* val;
204
0
205
0
  for (mSections.Get(aSection, &val);
206
0
       val;
207
0
       val = val->next.get()) {
208
0
209
0
    if (!aCB(val->key, val->value, aClosure)) {
210
0
      return NS_OK;
211
0
    }
212
0
  }
213
0
214
0
  return NS_OK;
215
0
}
216
217
nsresult
218
nsINIParser::SetString(const char* aSection, const char* aKey, const char* aValue)
219
0
{
220
0
  if (!IsValidSection(aSection) || !IsValidKey(aKey) || !IsValidValue(aValue)) {
221
0
    return NS_ERROR_INVALID_ARG;
222
0
  }
223
0
224
0
  INIValue* v;
225
0
  if (!mSections.Get(aSection, &v)) {
226
0
    v = new INIValue(aKey, aValue);
227
0
228
0
    mSections.Put(aSection, v);
229
0
    return NS_OK;
230
0
  }
231
0
232
0
  // Check whether this key has already been specified; overwrite
233
0
  // if so, or append if not.
234
0
  while (v) {
235
0
    if (!strcmp(aKey, v->key)) {
236
0
      v->SetValue(aValue);
237
0
      break;
238
0
    }
239
0
    if (!v->next) {
240
0
      v->next = MakeUnique<INIValue>(aKey, aValue);
241
0
      break;
242
0
    }
243
0
    v = v->next.get();
244
0
  }
245
0
  NS_ASSERTION(v, "v should never be null coming out of this loop");
246
0
247
0
  return NS_OK;
248
0
}
249
250
nsresult
251
nsINIParser::DeleteString(const char* aSection, const char* aKey)
252
0
{
253
0
  if (!IsValidSection(aSection) || !IsValidKey(aKey)) {
254
0
    return NS_ERROR_INVALID_ARG;
255
0
  }
256
0
257
0
  INIValue* val;
258
0
  if (!mSections.Get(aSection, &val)) {
259
0
    return NS_ERROR_FAILURE;
260
0
  }
261
0
262
0
  // Special case the first result
263
0
  if (strcmp(val->key, aKey) == 0) {
264
0
    if (!val->next) {
265
0
      mSections.Remove(aSection);
266
0
    } else {
267
0
      mSections.Put(aSection, val->next.release());
268
0
      delete val;
269
0
    }
270
0
    return NS_OK;
271
0
  }
272
0
273
0
  while (val->next) {
274
0
    if (strcmp(val->next->key, aKey) == 0) {
275
0
      val->next = std::move(val->next->next);
276
0
277
0
      return NS_OK;
278
0
    }
279
0
280
0
    val = val->next.get();
281
0
  }
282
0
283
0
  return NS_ERROR_FAILURE;
284
0
}
285
286
nsresult
287
nsINIParser::DeleteSection(const char* aSection)
288
0
{
289
0
  if (!IsValidSection(aSection)) {
290
0
    return NS_ERROR_INVALID_ARG;
291
0
  }
292
0
293
0
  if (!mSections.Remove(aSection)) {
294
0
    return NS_ERROR_FAILURE;
295
0
  }
296
0
  return NS_OK;
297
0
}
298
299
nsresult
300
0
nsINIParser::WriteToFile(nsIFile *aFile) {
301
0
  nsCString buffer;
302
0
303
0
  for (auto iter = mSections.Iter(); !iter.Done(); iter.Next()) {
304
0
    buffer.AppendPrintf("[%s]\n", iter.Key());
305
0
    INIValue* val = iter.Data();
306
0
    while (val) {
307
0
      buffer.AppendPrintf("%s=%s\n", val->key, val->value);
308
0
      val = val->next.get();
309
0
    }
310
0
    buffer.AppendLiteral("\n");
311
0
  }
312
0
313
0
  FILE* writeFile;
314
0
  nsresult rv = aFile->OpenANSIFileDesc("w", &writeFile);
315
0
  NS_ENSURE_SUCCESS(rv, rv);
316
0
317
0
  unsigned int length = buffer.Length();
318
0
319
0
  if (fwrite(buffer.get(), sizeof(char), length, writeFile) != length) {
320
0
      fclose(writeFile);
321
0
      return NS_ERROR_UNEXPECTED;
322
0
  }
323
0
324
0
  fclose(writeFile);
325
0
  return NS_OK;
326
0
}