1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is Google Inc.
19 : * Portions created by the Initial Developer are Copyright (C) 2005
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@meer.net>
24 : * Alex Pakhotin <alexp@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include <limits.h>
41 : #include <string.h>
42 : #include <stdio.h>
43 : #include "readstrings.h"
44 : #include "errors.h"
45 :
46 : #ifdef XP_WIN
47 : # define NS_tfopen _wfopen
48 : # define OPEN_MODE L"rb"
49 : #else
50 : # define NS_tfopen fopen
51 : # define OPEN_MODE "r"
52 : #endif
53 :
54 : // stack based FILE wrapper to ensure that fclose is called.
55 : class AutoFILE {
56 : public:
57 5 : AutoFILE(FILE *fp) : fp_(fp) {}
58 5 : ~AutoFILE() { if (fp_) fclose(fp_); }
59 21 : operator FILE *() { return fp_; }
60 : private:
61 : FILE *fp_;
62 : };
63 :
64 : class AutoCharArray {
65 : public:
66 4 : AutoCharArray(size_t len) { ptr_ = new char[len]; }
67 4 : ~AutoCharArray() { delete[] ptr_; }
68 16 : operator char *() { return ptr_; }
69 : private:
70 : char *ptr_;
71 : };
72 :
73 : static const char kNL[] = "\r\n";
74 : static const char kEquals[] = "=";
75 : static const char kWhitespace[] = " \t";
76 : static const char kRBracket[] = "]";
77 :
78 : static const char*
79 247 : NS_strspnp(const char *delims, const char *str)
80 : {
81 : const char *d;
82 247 : do {
83 636 : for (d = delims; *d != '\0'; ++d) {
84 469 : if (*str == *d) {
85 80 : ++str;
86 80 : break;
87 : }
88 : }
89 : } while (*d);
90 :
91 167 : return str;
92 : }
93 :
94 : static char*
95 125 : NS_strtok(const char *delims, char **str)
96 : {
97 125 : if (!*str)
98 0 : return NULL;
99 :
100 125 : char *ret = (char*) NS_strspnp(delims, *str);
101 :
102 125 : if (!*ret) {
103 16 : *str = ret;
104 16 : return NULL;
105 : }
106 :
107 109 : char *i = ret;
108 1396 : do {
109 4180 : for (const char *d = delims; *d != '\0'; ++d) {
110 2784 : if (*i == *d) {
111 109 : *i = '\0';
112 109 : *str = ++i;
113 109 : return ret;
114 : }
115 : }
116 1396 : ++i;
117 : } while (*i);
118 :
119 0 : *str = NULL;
120 0 : return ret;
121 : }
122 :
123 : /**
124 : * Find a key in a keyList containing zero-delimited keys ending with "\0\0".
125 : * Returns a zero-based index of the key in the list, or -1 if the key is not found.
126 : */
127 : static int
128 13 : find_key(const char *keyList, char* key)
129 : {
130 13 : if (!keyList)
131 0 : return -1;
132 :
133 13 : int index = 0;
134 13 : const char *p = keyList;
135 43 : while (*p)
136 : {
137 22 : if (strcmp(key, p) == 0)
138 5 : return index;
139 :
140 17 : p += strlen(p) + 1;
141 17 : index++;
142 : }
143 :
144 : // The key was not found if we came here
145 8 : return -1;
146 : }
147 :
148 : /**
149 : * A very basic parser for updater.ini taken mostly from nsINIParser.cpp
150 : * that can be used by standalone apps.
151 : *
152 : * @param path Path to the .ini file to read
153 : * @param keyList List of zero-delimited keys ending with two zero characters
154 : * @param numStrings Number of strings to read into results buffer - must be equal to the number of keys
155 : * @param results Two-dimensional array of strings to be filled in the same order as the keys provided
156 : * @param section Optional name of the section to read; defaults to "Strings"
157 : */
158 : int
159 5 : ReadStrings(const NS_tchar *path,
160 : const char *keyList,
161 : unsigned int numStrings,
162 : char results[][MAX_TEXT_LEN],
163 : const char *section)
164 : {
165 10 : AutoFILE fp = NS_tfopen(path, OPEN_MODE);
166 :
167 5 : if (!fp)
168 1 : return READ_ERROR;
169 :
170 : /* get file size */
171 4 : if (fseek(fp, 0, SEEK_END) != 0)
172 0 : return READ_ERROR;
173 :
174 4 : long len = ftell(fp);
175 4 : if (len <= 0)
176 0 : return READ_ERROR;
177 :
178 4 : size_t flen = size_t(len);
179 8 : AutoCharArray fileContents(flen + 1);
180 4 : if (!fileContents)
181 0 : return READ_STRINGS_MEM_ERROR;
182 :
183 : /* read the file in one swoop */
184 4 : if (fseek(fp, 0, SEEK_SET) != 0)
185 0 : return READ_ERROR;
186 :
187 4 : size_t rd = fread(fileContents, sizeof(char), flen, fp);
188 4 : if (rd != flen)
189 0 : return READ_ERROR;
190 :
191 4 : fileContents[flen] = '\0';
192 :
193 4 : char *buffer = fileContents;
194 4 : bool inStringsSection = false;
195 :
196 4 : unsigned int read = 0;
197 :
198 172 : while (char *token = NS_strtok(kNL, &buffer)) {
199 84 : if (token[0] == '#' || token[0] == ';') // it's a comment
200 42 : continue;
201 :
202 42 : token = (char*) NS_strspnp(kWhitespace, token);
203 42 : if (!*token) // empty line
204 0 : continue;
205 :
206 42 : if (token[0] == '[') { // section header!
207 12 : ++token;
208 12 : char const * currSection = token;
209 :
210 12 : char *rb = NS_strtok(kRBracket, &token);
211 12 : if (!rb || NS_strtok(kWhitespace, &token)) {
212 : // there's either an unclosed [Section or a [Section]Moretext!
213 : // we could frankly decide that this INI file is malformed right
214 : // here and stop, but we won't... keep going, looking for
215 : // a well-formed [section] to continue working with
216 0 : inStringsSection = false;
217 : }
218 : else {
219 12 : if (section)
220 3 : inStringsSection = strcmp(currSection, section) == 0;
221 : else
222 9 : inStringsSection = strcmp(currSection, "Strings") == 0;
223 : }
224 :
225 12 : continue;
226 : }
227 :
228 30 : if (!inStringsSection) {
229 : // If we haven't found a section header (or we found a malformed
230 : // section header), or this isn't the [Strings] section don't bother
231 : // parsing this line.
232 17 : continue;
233 : }
234 :
235 13 : char *key = token;
236 13 : char *e = NS_strtok(kEquals, &token);
237 13 : if (!e)
238 0 : continue;
239 :
240 13 : int keyIndex = find_key(keyList, key);
241 13 : if (keyIndex >= 0 && (unsigned int)keyIndex < numStrings)
242 : {
243 5 : strncpy(results[keyIndex], token, MAX_TEXT_LEN - 1);
244 5 : results[keyIndex][MAX_TEXT_LEN - 1] = '\0';
245 5 : read++;
246 : }
247 : }
248 :
249 6 : return (read == numStrings) ? OK : PARSE_ERROR;
250 : }
251 :
252 : // A wrapper function to read strings for the updater.
253 : // Added for compatibility with the original code.
254 : int
255 4 : ReadStrings(const NS_tchar *path, StringTable *results)
256 : {
257 4 : const unsigned int kNumStrings = 2;
258 4 : const char *kUpdaterKeys = "Title\0Info\0";
259 : char updater_strings[kNumStrings][MAX_TEXT_LEN];
260 :
261 4 : int result = ReadStrings(path, kUpdaterKeys, kNumStrings, updater_strings);
262 :
263 4 : strncpy(results->title, updater_strings[0], MAX_TEXT_LEN - 1);
264 4 : results->title[MAX_TEXT_LEN - 1] = '\0';
265 4 : strncpy(results->info, updater_strings[1], MAX_TEXT_LEN - 1);
266 4 : results->info[MAX_TEXT_LEN - 1] = '\0';
267 :
268 4 : return result;
269 : }
|