/** My kind of ini file loader, like used in the PWRTS. Sections are case sensitive. Names are not, and spaces are eliminated. ; and # are both comments = { ... } is a multiline value */ import std.stdio; import std.string; /** Loads an ini style file, calling the delegates when it encounters a data. It loads the file filename, which should be in a format of [section] # comment name = value ; comment name = { multi line value } It calls the delegate encounteredSection, passing it the name of the section as an argument. This is case sensitive, so if the file has [section] it will call encounteredSection("section"); and if it says [Section] it will call encounteredSection("Section");. It calls the delegate encounteredValue with the name / value pair when it encounters one, cutting off leading and trailing whitespace of the value. Whitespace in the middle of the value is preserved. The name passed is converted to lowercase and has all whitespace stripped from it. So if it encounters: some Name = Hello It will call encounteredValue("somename", "Hello");. It does not currently pass the current section name to the encounteredValue function in any way. As of this version, it is your responsibility to keep track of that if you need it. */ void loadINI(char[] filename, void delegate(char[] name) encounteredSection, void delegate(char[] name, char[] value) encounteredValue ){ char[] stripComment(char[] li){ int a; char[] l; for(a = 0; a < li.length; a++) if(li[a] == '#' || li[a] == ';' || li[a] == 10 || li[a] == 13) break; l = li[0..a]; return l; } FILE* fp; fp = fopen(std.string.toStringz(filename), "rb"); if(fp is null) throw new Exception("Unable to open ini file: " ~ filename); scope(exit) fclose(fp); bool grabbing; char[] grabbingBuffer; char[] grabbingv; for(char[] line = readln(fp); !feof(fp); line = readln(fp)){ if(grabbing){ char[] l = strip(line); if(l[0] == '}'){ encounteredValue(grabbingv, grabbingBuffer); grabbingBuffer = ""; grabbingv = ""; grabbing = false; continue; }else{ grabbingBuffer ~= l ~ "\n"; continue; } } line = strip(line); if(line.length == 0) continue; if(line[0] == '\r' || line[0] == '\n' || line[0] == '#' || line[0] == ';') continue; line = stripComment(line); line = strip(line); if(line.length < 1) continue; if(line[0] == '['){ char[] s = line[1..(length-1)]; encounteredSection(s); continue; } char[][] words = split(line, "="); char[] arg = tolower(removechars(words[0], " \t")); char[] val = strip(words[1]); if(val[0] == '{'){ grabbingv = arg; grabbing = true; } else encounteredValue(arg, val); } } /** toBool: attempts to convert a string that might be found in the ini to a boolean value. See the source for its details; this function is only about 15 lines long. */ bool toBool(char[] str){ switch(tolower(str)){ case "yes": case "true": case "t": case "y": case "1": return true; case "no": case "false": case "n": case "f": case "0": return false; } }