1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/mozapps/update/common/readstrings.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,236 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <limits.h> 1.11 +#include <string.h> 1.12 +#include <stdio.h> 1.13 +#include "readstrings.h" 1.14 +#include "errors.h" 1.15 + 1.16 +#ifdef XP_WIN 1.17 +# define NS_tfopen _wfopen 1.18 +# define OPEN_MODE L"rb" 1.19 +#else 1.20 +# define NS_tfopen fopen 1.21 +# define OPEN_MODE "r" 1.22 +#endif 1.23 + 1.24 +// stack based FILE wrapper to ensure that fclose is called. 1.25 +class AutoFILE { 1.26 +public: 1.27 + AutoFILE(FILE *fp) : fp_(fp) {} 1.28 + ~AutoFILE() { if (fp_) fclose(fp_); } 1.29 + operator FILE *() { return fp_; } 1.30 +private: 1.31 + FILE *fp_; 1.32 +}; 1.33 + 1.34 +class AutoCharArray { 1.35 +public: 1.36 + AutoCharArray(size_t len) { ptr_ = new char[len]; } 1.37 + ~AutoCharArray() { delete[] ptr_; } 1.38 + operator char *() { return ptr_; } 1.39 +private: 1.40 + char *ptr_; 1.41 +}; 1.42 + 1.43 +static const char kNL[] = "\r\n"; 1.44 +static const char kEquals[] = "="; 1.45 +static const char kWhitespace[] = " \t"; 1.46 +static const char kRBracket[] = "]"; 1.47 + 1.48 +static const char* 1.49 +NS_strspnp(const char *delims, const char *str) 1.50 +{ 1.51 + const char *d; 1.52 + do { 1.53 + for (d = delims; *d != '\0'; ++d) { 1.54 + if (*str == *d) { 1.55 + ++str; 1.56 + break; 1.57 + } 1.58 + } 1.59 + } while (*d); 1.60 + 1.61 + return str; 1.62 +} 1.63 + 1.64 +static char* 1.65 +NS_strtok(const char *delims, char **str) 1.66 +{ 1.67 + if (!*str) 1.68 + return nullptr; 1.69 + 1.70 + char *ret = (char*) NS_strspnp(delims, *str); 1.71 + 1.72 + if (!*ret) { 1.73 + *str = ret; 1.74 + return nullptr; 1.75 + } 1.76 + 1.77 + char *i = ret; 1.78 + do { 1.79 + for (const char *d = delims; *d != '\0'; ++d) { 1.80 + if (*i == *d) { 1.81 + *i = '\0'; 1.82 + *str = ++i; 1.83 + return ret; 1.84 + } 1.85 + } 1.86 + ++i; 1.87 + } while (*i); 1.88 + 1.89 + *str = nullptr; 1.90 + return ret; 1.91 +} 1.92 + 1.93 +/** 1.94 + * Find a key in a keyList containing zero-delimited keys ending with "\0\0". 1.95 + * Returns a zero-based index of the key in the list, or -1 if the key is not found. 1.96 + */ 1.97 +static int 1.98 +find_key(const char *keyList, char* key) 1.99 +{ 1.100 + if (!keyList) 1.101 + return -1; 1.102 + 1.103 + int index = 0; 1.104 + const char *p = keyList; 1.105 + while (*p) 1.106 + { 1.107 + if (strcmp(key, p) == 0) 1.108 + return index; 1.109 + 1.110 + p += strlen(p) + 1; 1.111 + index++; 1.112 + } 1.113 + 1.114 + // The key was not found if we came here 1.115 + return -1; 1.116 +} 1.117 + 1.118 +/** 1.119 + * A very basic parser for updater.ini taken mostly from nsINIParser.cpp 1.120 + * that can be used by standalone apps. 1.121 + * 1.122 + * @param path Path to the .ini file to read 1.123 + * @param keyList List of zero-delimited keys ending with two zero characters 1.124 + * @param numStrings Number of strings to read into results buffer - must be equal to the number of keys 1.125 + * @param results Two-dimensional array of strings to be filled in the same order as the keys provided 1.126 + * @param section Optional name of the section to read; defaults to "Strings" 1.127 + */ 1.128 +int 1.129 +ReadStrings(const NS_tchar *path, 1.130 + const char *keyList, 1.131 + unsigned int numStrings, 1.132 + char results[][MAX_TEXT_LEN], 1.133 + const char *section) 1.134 +{ 1.135 + AutoFILE fp = NS_tfopen(path, OPEN_MODE); 1.136 + 1.137 + if (!fp) 1.138 + return READ_ERROR; 1.139 + 1.140 + /* get file size */ 1.141 + if (fseek(fp, 0, SEEK_END) != 0) 1.142 + return READ_ERROR; 1.143 + 1.144 + long len = ftell(fp); 1.145 + if (len <= 0) 1.146 + return READ_ERROR; 1.147 + 1.148 + size_t flen = size_t(len); 1.149 + AutoCharArray fileContents(flen + 1); 1.150 + if (!fileContents) 1.151 + return READ_STRINGS_MEM_ERROR; 1.152 + 1.153 + /* read the file in one swoop */ 1.154 + if (fseek(fp, 0, SEEK_SET) != 0) 1.155 + return READ_ERROR; 1.156 + 1.157 + size_t rd = fread(fileContents, sizeof(char), flen, fp); 1.158 + if (rd != flen) 1.159 + return READ_ERROR; 1.160 + 1.161 + fileContents[flen] = '\0'; 1.162 + 1.163 + char *buffer = fileContents; 1.164 + bool inStringsSection = false; 1.165 + 1.166 + unsigned int read = 0; 1.167 + 1.168 + while (char *token = NS_strtok(kNL, &buffer)) { 1.169 + if (token[0] == '#' || token[0] == ';') // it's a comment 1.170 + continue; 1.171 + 1.172 + token = (char*) NS_strspnp(kWhitespace, token); 1.173 + if (!*token) // empty line 1.174 + continue; 1.175 + 1.176 + if (token[0] == '[') { // section header! 1.177 + ++token; 1.178 + char const * currSection = token; 1.179 + 1.180 + char *rb = NS_strtok(kRBracket, &token); 1.181 + if (!rb || NS_strtok(kWhitespace, &token)) { 1.182 + // there's either an unclosed [Section or a [Section]Moretext! 1.183 + // we could frankly decide that this INI file is malformed right 1.184 + // here and stop, but we won't... keep going, looking for 1.185 + // a well-formed [section] to continue working with 1.186 + inStringsSection = false; 1.187 + } 1.188 + else { 1.189 + if (section) 1.190 + inStringsSection = strcmp(currSection, section) == 0; 1.191 + else 1.192 + inStringsSection = strcmp(currSection, "Strings") == 0; 1.193 + } 1.194 + 1.195 + continue; 1.196 + } 1.197 + 1.198 + if (!inStringsSection) { 1.199 + // If we haven't found a section header (or we found a malformed 1.200 + // section header), or this isn't the [Strings] section don't bother 1.201 + // parsing this line. 1.202 + continue; 1.203 + } 1.204 + 1.205 + char *key = token; 1.206 + char *e = NS_strtok(kEquals, &token); 1.207 + if (!e) 1.208 + continue; 1.209 + 1.210 + int keyIndex = find_key(keyList, key); 1.211 + if (keyIndex >= 0 && (unsigned int)keyIndex < numStrings) 1.212 + { 1.213 + strncpy(results[keyIndex], token, MAX_TEXT_LEN - 1); 1.214 + results[keyIndex][MAX_TEXT_LEN - 1] = '\0'; 1.215 + read++; 1.216 + } 1.217 + } 1.218 + 1.219 + return (read == numStrings) ? OK : PARSE_ERROR; 1.220 +} 1.221 + 1.222 +// A wrapper function to read strings for the updater. 1.223 +// Added for compatibility with the original code. 1.224 +int 1.225 +ReadStrings(const NS_tchar *path, StringTable *results) 1.226 +{ 1.227 + const unsigned int kNumStrings = 2; 1.228 + const char *kUpdaterKeys = "Title\0Info\0"; 1.229 + char updater_strings[kNumStrings][MAX_TEXT_LEN]; 1.230 + 1.231 + int result = ReadStrings(path, kUpdaterKeys, kNumStrings, updater_strings); 1.232 + 1.233 + strncpy(results->title, updater_strings[0], MAX_TEXT_LEN - 1); 1.234 + results->title[MAX_TEXT_LEN - 1] = '\0'; 1.235 + strncpy(results->info, updater_strings[1], MAX_TEXT_LEN - 1); 1.236 + results->info[MAX_TEXT_LEN - 1] = '\0'; 1.237 + 1.238 + return result; 1.239 +}