michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include "readstrings.h" michael@0: #include "errors.h" michael@0: michael@0: #ifdef XP_WIN michael@0: # define NS_tfopen _wfopen michael@0: # define OPEN_MODE L"rb" michael@0: #else michael@0: # define NS_tfopen fopen michael@0: # define OPEN_MODE "r" michael@0: #endif michael@0: michael@0: // stack based FILE wrapper to ensure that fclose is called. michael@0: class AutoFILE { michael@0: public: michael@0: AutoFILE(FILE *fp) : fp_(fp) {} michael@0: ~AutoFILE() { if (fp_) fclose(fp_); } michael@0: operator FILE *() { return fp_; } michael@0: private: michael@0: FILE *fp_; michael@0: }; michael@0: michael@0: class AutoCharArray { michael@0: public: michael@0: AutoCharArray(size_t len) { ptr_ = new char[len]; } michael@0: ~AutoCharArray() { delete[] ptr_; } michael@0: operator char *() { return ptr_; } michael@0: private: michael@0: char *ptr_; michael@0: }; michael@0: michael@0: static const char kNL[] = "\r\n"; michael@0: static const char kEquals[] = "="; michael@0: static const char kWhitespace[] = " \t"; michael@0: static const char kRBracket[] = "]"; michael@0: michael@0: static const char* michael@0: NS_strspnp(const char *delims, const char *str) michael@0: { michael@0: const char *d; michael@0: do { michael@0: for (d = delims; *d != '\0'; ++d) { michael@0: if (*str == *d) { michael@0: ++str; michael@0: break; michael@0: } michael@0: } michael@0: } while (*d); michael@0: michael@0: return str; michael@0: } michael@0: michael@0: static char* michael@0: NS_strtok(const char *delims, char **str) michael@0: { michael@0: if (!*str) michael@0: return nullptr; michael@0: michael@0: char *ret = (char*) NS_strspnp(delims, *str); michael@0: michael@0: if (!*ret) { michael@0: *str = ret; michael@0: return nullptr; michael@0: } michael@0: michael@0: char *i = ret; michael@0: do { michael@0: for (const char *d = delims; *d != '\0'; ++d) { michael@0: if (*i == *d) { michael@0: *i = '\0'; michael@0: *str = ++i; michael@0: return ret; michael@0: } michael@0: } michael@0: ++i; michael@0: } while (*i); michael@0: michael@0: *str = nullptr; michael@0: return ret; michael@0: } michael@0: michael@0: /** michael@0: * Find a key in a keyList containing zero-delimited keys ending with "\0\0". michael@0: * Returns a zero-based index of the key in the list, or -1 if the key is not found. michael@0: */ michael@0: static int michael@0: find_key(const char *keyList, char* key) michael@0: { michael@0: if (!keyList) michael@0: return -1; michael@0: michael@0: int index = 0; michael@0: const char *p = keyList; michael@0: while (*p) michael@0: { michael@0: if (strcmp(key, p) == 0) michael@0: return index; michael@0: michael@0: p += strlen(p) + 1; michael@0: index++; michael@0: } michael@0: michael@0: // The key was not found if we came here michael@0: return -1; michael@0: } michael@0: michael@0: /** michael@0: * A very basic parser for updater.ini taken mostly from nsINIParser.cpp michael@0: * that can be used by standalone apps. michael@0: * michael@0: * @param path Path to the .ini file to read michael@0: * @param keyList List of zero-delimited keys ending with two zero characters michael@0: * @param numStrings Number of strings to read into results buffer - must be equal to the number of keys michael@0: * @param results Two-dimensional array of strings to be filled in the same order as the keys provided michael@0: * @param section Optional name of the section to read; defaults to "Strings" michael@0: */ michael@0: int michael@0: ReadStrings(const NS_tchar *path, michael@0: const char *keyList, michael@0: unsigned int numStrings, michael@0: char results[][MAX_TEXT_LEN], michael@0: const char *section) michael@0: { michael@0: AutoFILE fp = NS_tfopen(path, OPEN_MODE); michael@0: michael@0: if (!fp) michael@0: return READ_ERROR; michael@0: michael@0: /* get file size */ michael@0: if (fseek(fp, 0, SEEK_END) != 0) michael@0: return READ_ERROR; michael@0: michael@0: long len = ftell(fp); michael@0: if (len <= 0) michael@0: return READ_ERROR; michael@0: michael@0: size_t flen = size_t(len); michael@0: AutoCharArray fileContents(flen + 1); michael@0: if (!fileContents) michael@0: return READ_STRINGS_MEM_ERROR; michael@0: michael@0: /* read the file in one swoop */ michael@0: if (fseek(fp, 0, SEEK_SET) != 0) michael@0: return READ_ERROR; michael@0: michael@0: size_t rd = fread(fileContents, sizeof(char), flen, fp); michael@0: if (rd != flen) michael@0: return READ_ERROR; michael@0: michael@0: fileContents[flen] = '\0'; michael@0: michael@0: char *buffer = fileContents; michael@0: bool inStringsSection = false; michael@0: michael@0: unsigned int read = 0; michael@0: michael@0: while (char *token = NS_strtok(kNL, &buffer)) { michael@0: if (token[0] == '#' || token[0] == ';') // it's a comment michael@0: continue; michael@0: michael@0: token = (char*) NS_strspnp(kWhitespace, token); michael@0: if (!*token) // empty line michael@0: continue; michael@0: michael@0: if (token[0] == '[') { // section header! michael@0: ++token; michael@0: char const * currSection = token; michael@0: michael@0: char *rb = NS_strtok(kRBracket, &token); michael@0: if (!rb || NS_strtok(kWhitespace, &token)) { michael@0: // there's either an unclosed [Section or a [Section]Moretext! michael@0: // we could frankly decide that this INI file is malformed right michael@0: // here and stop, but we won't... keep going, looking for michael@0: // a well-formed [section] to continue working with michael@0: inStringsSection = false; michael@0: } michael@0: else { michael@0: if (section) michael@0: inStringsSection = strcmp(currSection, section) == 0; michael@0: else michael@0: inStringsSection = strcmp(currSection, "Strings") == 0; michael@0: } michael@0: michael@0: continue; michael@0: } michael@0: michael@0: if (!inStringsSection) { michael@0: // If we haven't found a section header (or we found a malformed michael@0: // section header), or this isn't the [Strings] section don't bother michael@0: // parsing this line. michael@0: continue; michael@0: } michael@0: michael@0: char *key = token; michael@0: char *e = NS_strtok(kEquals, &token); michael@0: if (!e) michael@0: continue; michael@0: michael@0: int keyIndex = find_key(keyList, key); michael@0: if (keyIndex >= 0 && (unsigned int)keyIndex < numStrings) michael@0: { michael@0: strncpy(results[keyIndex], token, MAX_TEXT_LEN - 1); michael@0: results[keyIndex][MAX_TEXT_LEN - 1] = '\0'; michael@0: read++; michael@0: } michael@0: } michael@0: michael@0: return (read == numStrings) ? OK : PARSE_ERROR; michael@0: } michael@0: michael@0: // A wrapper function to read strings for the updater. michael@0: // Added for compatibility with the original code. michael@0: int michael@0: ReadStrings(const NS_tchar *path, StringTable *results) michael@0: { michael@0: const unsigned int kNumStrings = 2; michael@0: const char *kUpdaterKeys = "Title\0Info\0"; michael@0: char updater_strings[kNumStrings][MAX_TEXT_LEN]; michael@0: michael@0: int result = ReadStrings(path, kUpdaterKeys, kNumStrings, updater_strings); michael@0: michael@0: strncpy(results->title, updater_strings[0], MAX_TEXT_LEN - 1); michael@0: results->title[MAX_TEXT_LEN - 1] = '\0'; michael@0: strncpy(results->info, updater_strings[1], MAX_TEXT_LEN - 1); michael@0: results->info[MAX_TEXT_LEN - 1] = '\0'; michael@0: michael@0: return result; michael@0: }