const std::string replaceEnvVars(const std::string& str) { std::string ret_str(str); ret_str = replaceEnvVars(ret_str, std::string("${"), std::string("}") ); ret_str = replaceEnvVars(ret_str, std::string("$("), std::string(")") ); return ret_str; }
bool ConfigMap::readFile(const std::string &filename) { std::string instr; if (MinVR::FileSystem::getInstance().exists(filename)) { std::string output = "ConfigMap parsing file \"" + filename + "\"."; std::cout << output << std::endl; ifstream fIn; fIn.open(filename.c_str(),std::ios::in); if (!fIn) { MinVR::Logger::getInstance().assertMessage(false, "ConfigMap Error: Unable to load config file"); } std::stringstream ss; ss << fIn.rdbuf(); instr = ss.str(); } else { std::stringstream ss; ss << "Cannot locate config file " << filename; MinVR::Logger::getInstance().assertMessage(false, ss.str().c_str()); } // convert all endline characters to \n's for (int i=0;i<instr.size();i++) { if (instr[i] == '\r') { instr[i] = '\n'; } } // remove any cases of two \n's next to each other for (int i=0;i<instr.size()-1;i++) { if ((instr[i] == '\n') && (instr[i+1] == '\n')) { instr = instr.substr(0,i) + instr.substr(i+1); } } // add a \n so that every file ends in at least one \n instr = instr + std::string("\n"); // start pulling out name, value pairs // the config file must end with a newline // while (instr.size()) { int endline = instr.find("\n"); std::string nameval = instr.substr(0,endline); // a backslash not followed by a second backslash means continue on // the next line, wipe out everything from the backslash to the newline /** Note, this will miss the following scenario, which will probably never occur in practice: myname myvalue with a single slash here \\ and \ continued on the next line like this So, this doesn't handle a \\ on the same line as a \ **/ int slash = nameval.find("\\"); bool nextCharIsSlash = false; if (slash < nameval.size() - 1) { nextCharIsSlash = (nameval[slash+1] == '\\'); } else { nextCharIsSlash = false; } while ((slash != nameval.npos) && !nextCharIsSlash && (endline != nameval.npos)) { std::string fromPrevLine = nameval.substr(0,slash); instr = instr.substr(endline+1); endline = instr.find("\n"); nameval = fromPrevLine + instr.substr(0,endline); slash = nameval.find("\\"); if (slash < nameval.size() - 1) { nextCharIsSlash = (nameval[slash+1] == '\\'); } else { nextCharIsSlash = false; } } // a line starting with # is a comment, ignore it // if (nameval.size() > 0 && nameval[0] != '#') { // if we have two backslashes \\ treat this as an escape sequence for // a single backslash, so replace the two with one int doubleslash = nameval.find("\\\\"); if (doubleslash >= 0) { nameval = nameval.substr(0,doubleslash) + nameval.substr(doubleslash + 1); } // replace all $(NAME) sequences with the value of the environment // variable NAME. under cygwin, cygwin-style paths are replaced with // windows style paths. nameval = replaceEnvVars(nameval); int firstspace = nameval.find(" "); int firsttab = nameval.find('\t'); if (((firsttab >=0) && (firsttab < firstspace)) || ((firsttab >=0) && (firstspace < 0))) { firstspace = firsttab; } std::string name = nameval.substr(0,firstspace); std::string val; if (firstspace >= 0) { val = trimWhitespace(nameval.substr(firstspace + 1)); } if (name != "") { if ((name.size() > 2) && (name[name.size()-2] == '+') && (name[name.size()-1] == '=')) { name = name.substr(0,name.size()-2); if (ConfigMap::containsKey(name)) { std::string newVal = ConfigMap::getValue(name) + " " + val; ConfigMap::set(name, newVal); } else { ConfigMap::set(name, val); } } else { ConfigMap::set(name, val); } } } // end not a comment instr = instr.substr(endline+1); } return true; }