void StreamTokenizer::skipChunk(SeekableReadStream &stream) { assert(!_chunkEnds.empty()); while (!stream.eos() && !stream.err()) { if (isIn(stream.readByte(), _chunkEnds)) { stream.seek(-1, SEEK_CUR); break; } } if (stream.err()) throw Exception(kReadError); }
void StreamTokenizer::nextChunk(SeekableReadStream &stream) { skipChunk(stream); byte c = stream.readByte(); if (stream.eos() || stream.err()) return; if (!isIn(c, _chunkEnds)) stream.seek(-1, SEEK_CUR); else if (stream.pos() == stream.size()) // This actually the last character, read one more byte to properly set the EOS state stream.readByte(); }
bool ConfigFile::loadFromStream(SeekableReadStream &stream) { Section section; KeyValue kv; String comment; int lineno = 0; // TODO: Detect if a section occurs multiple times (or likewise, if // a key occurs multiple times inside one section). while (!stream.eos() && !stream.err()) { lineno++; // Read a line String line = stream.readLine(); if (line.size() == 0) { // Do nothing } else if (line[0] == '#' || line[0] == ';' || line.hasPrefix("//")) { // Accumulate comments here. Once we encounter either the start // of a new section, or a key-value-pair, we associate the value // of the 'comment' variable with that entity. The semicolon and // C++-style comments are used for Living Books games in Mohawk. comment += line; comment += "\n"; } else if (line[0] == '(') { // HACK: The following is a hack added by Kirben to support the // "map.ini" used in the HE SCUMM game "SPY Fox in Hold the Mustard". // // It would be nice if this hack could be restricted to that game, // but the current design of this class doesn't allow to do that // in a nice fashion (a "isMustard" parameter is *not* a nice // solution). comment += line; comment += "\n"; } else if (line[0] == '[') { // It's a new section which begins here. const char *p = line.c_str() + 1; // Get the section name, and check whether it's valid (that // is, verify that it only consists of alphanumerics, // periods, dashes and underscores). Mohawk Living Books games // can have periods in their section names. while (*p && (isalnum(static_cast<unsigned char>(*p)) || *p == '-' || *p == '_' || *p == '.')) p++; if (*p == '\0') error("ConfigFile::loadFromStream: missing ] in line %d", lineno); else if (*p != ']') error("ConfigFile::loadFromStream: Invalid character '%c' occurred in section name in line %d", *p, lineno); // Previous section is finished now, store it. if (!section.name.empty()) _sections.push_back(section); section.name = String(line.c_str() + 1, p); section.keys.clear(); section.comment = comment; comment.clear(); assert(isValidName(section.name)); } else { // This line should be a line with a 'key=value' pair, or an empty one. // Skip leading whitespaces const char *t = line.c_str(); while (isspace(static_cast<unsigned char>(*t))) t++; // Skip empty lines / lines with only whitespace if (*t == 0) continue; // If no section has been set, this config file is invalid! if (section.name.empty()) { error("ConfigFile::loadFromStream: Key/value pair found outside a section in line %d", lineno); } // Split string at '=' into 'key' and 'value'. First, find the "=" delimeter. const char *p = strchr(t, '='); if (!p) error("Config file buggy: Junk found in line line %d: '%s'", lineno, t); // Extract the key/value pair kv.key = String(t, p); kv.value = String(p + 1); // Trim of spaces kv.key.trim(); kv.value.trim(); // Store comment kv.comment = comment; comment.clear(); assert(isValidName(kv.key)); section.keys.push_back(kv); } } // Save last section if (!section.name.empty()) _sections.push_back(section); return (!stream.err() || stream.eos()); }
void ConfigManager::loadFromStream(SeekableReadStream &stream) { String domainName; String comment; Domain domain; int lineno = 0; _appDomain.clear(); _gameDomains.clear(); _miscDomains.clear(); _transientDomain.clear(); _domainSaveOrder.clear(); #ifdef ENABLE_KEYMAPPER _keymapperDomain.clear(); #endif // TODO: Detect if a domain occurs multiple times (or likewise, if // a key occurs multiple times inside one domain). while (!stream.eos() && !stream.err()) { lineno++; // Read a line String line = stream.readLine(); if (line.size() == 0) { // Do nothing } else if (line[0] == '#') { // Accumulate comments here. Once we encounter either the start // of a new domain, or a key-value-pair, we associate the value // of the 'comment' variable with that entity. comment += line; #ifdef _WIN32 comment += "\r\n"; #else comment += "\n"; #endif } else if (line[0] == '[') { // It's a new domain which begins here. // Determine where the previously accumulated domain goes, if we accumulated anything. addDomain(domainName, domain); domain.clear(); const char *p = line.c_str() + 1; // Get the domain name, and check whether it's valid (that // is, verify that it only consists of alphanumerics, // dashes and underscores). while (*p && (isalnum(*p) || *p == '-' || *p == '_')) p++; if (*p == '\0') error("Config file buggy: missing ] in line %d", lineno); else if (*p != ']') error("Config file buggy: Invalid character '%c' occurred in section name in line %d", *p, lineno); domainName = String(line.c_str() + 1, p); domain.setDomainComment(comment); comment.clear(); } else { // This line should be a line with a 'key=value' pair, or an empty one. // Skip leading whitespaces const char *t = line.c_str(); while (isspace(*t)) t++; // Skip empty lines / lines with only whitespace if (*t == 0) continue; // If no domain has been set, this config file is invalid! if (domainName.empty()) { error("Config file buggy: Key/value pair found outside a domain in line %d", lineno); } // Split string at '=' into 'key' and 'value'. First, find the "=" delimeter. const char *p = strchr(t, '='); if (!p) error("Config file buggy: Junk found in line line %d: '%s'", lineno, t); // Extract the key/value pair String key(t, p); String value(p + 1); // Trim of spaces key.trim(); value.trim(); // Finally, store the key/value pair in the active domain domain[key] = value; // Store comment domain.setKVComment(key, comment); comment.clear(); } } addDomain(domainName, domain); // Add the last domain found }
void ConfigFile::load(SeekableReadStream &stream) { UString comment; ConfigDomain *domain = 0; int lineNumber = 0; int domainLineNumber = 0; while (!stream.eos() && !stream.err()) { lineNumber++; // Read a line UString line; line.readLineUTF8(stream); // Parse it UString domainName; UString key, value, lineComment; parseConfigLine(line, domainName, key, value, lineComment, lineNumber); if (!domainName.empty()) { // New domain // Finish up the old domain addDomain(domain, domainLineNumber); // Check that the name is actually valid if (!isValidName(domainName)) throw Exception("\"%s\" isn't a valid domain name (line %d)", domainName.c_str(), lineNumber); // Create the new domain domain = new ConfigDomain(domainName); domain->_prologue = comment; domain->_comment = lineComment; comment.clear(); lineComment.clear(); domainLineNumber = lineNumber; } if (!key.empty()) { // New key if (!domain) throw Exception("Found a key outside a domain (line %d)", lineNumber); if (!isValidName(key)) throw Exception("\"%s\" isn't a valid key name (line %d)", key.c_str(), lineNumber); // Add collected comments to the domain if (!comment.empty()) addDomainKey(*domain, "", "", comment, lineNumber); // Add the key to the domain addDomainKey(*domain, key, value, lineComment, lineNumber); comment.clear(); lineComment.clear(); } // Collect comments, we don't yet know where those belong to. if (!lineComment.empty()) { if (!comment.empty()) comment += '\n'; comment += lineComment; } // Empty line, associate collected comments with the current domain if (domainName.empty() && key.empty() && value.empty() && lineComment.empty()) { if (!comment.empty() && !stream.eos()) { if (!domain) { // We have no domain yet, add it to the file's prologue if (!_prologue.empty()) _prologue += '\n'; _prologue += comment; } else addDomainKey(*domain, "", "", comment, lineNumber); comment.clear(); } } } if (stream.err()) throw Exception(kReadError); // Finish up the last domain addDomain(domain, domainLineNumber); // We still have comments, those apparently belong to the bottom of the file if (!comment.empty()) _epilogue = comment; }