Changeset 95
- Timestamp:
- 02/05/07 06:33:05 (2 years ago)
- Files:
-
- misc/configparse.d (modified) (15 diffs)
- misc/configparse.html (added)
- misc/configtest.d (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
misc/configparse.d
r94 r95 26 26 27 27 import std.stream : Stream, BufferedFile, FileMode; 28 import std.string : find, strip, toString, tolower ;28 import std.string : find, strip, toString, tolower, split; 29 29 import std.file : exists; 30 30 import std.conv : toInt, toReal, ConvError; 31 import std.path : getBaseName, getDirName, join, pathsep; 32 import std.c.stdlib : getenv; 31 33 32 34 import std.stdio : writefln; 33 35 34 // Thrown when configparse is unable to parse a file.36 /// Thrown when configparse is unable to parse a file. 35 37 class ConfigParseError : Exception { 36 38 this(char[] msg) { super(msg); } 37 39 } 38 // Thrown when configparse is unable to coerce a value to a requested type.40 /// Thrown when configparse is unable to coerce a value to a requested type. 39 41 class ConfigConvertError : Exception { 40 42 this(char[] msg) { super(msg); } 41 43 } 42 // Thrown when user adds already-existing section to parser.44 /// Thrown when user adds already-existing section to parser. 43 45 class DuplicateSectionError : Exception { 44 46 this(char[] msg) { super(msg); } 45 47 } 46 // Thrown when a section is not found.48 /// Thrown when a section is not found. 47 49 class SectionNotFoundError : Exception { 48 50 this(char[] msg) { super(msg); } 49 51 } 50 // Thrown when an option is not found.52 /// Thrown when an option is not found. 51 53 class OptionNotFoundError : Exception { 52 54 this(char[] msg) { super(msg); } 53 55 } 54 56 57 /// The name of the global section. This is currently "General". 55 58 const char[] GLOBAL_SECTION = "General"; 56 59 60 // Returns the name of the executable, minus directories or extensions. 61 char[] get_program_name(char[] path) { 62 version(Windows) { 63 // (Unicode note: ".exe" only contains 4 code units, so this slice 64 // should Just Work.) (Although it remains to be seen how robust 65 // this code actually is.) 66 assert(path[$-4 .. $] == ".exe"); 67 path = path[0 .. $-4]; 68 } 69 return getBaseName(path); 70 } 71 char[] env(char* var) { 72 return toString(getenv(var)); 73 } 74 /++ 75 Returns the path of a config file (filename) in the same directory as the 76 executable (arg0). 77 +/ 78 char[] same_dir(char[] arg0, char[] filename) { 79 version (Windows) { 80 return join(getDirName(arg0), filename); 81 } else { 82 char[] dir = getDirName(arg0); 83 if (dir == "") { 84 char[][] paths = split(env("PATH"), pathsep); 85 foreach (path; paths) { 86 if (exists(join(path, getBaseName(arg0)))) 87 return join(path, filename); 88 } 89 return ""; 90 } else { 91 return join(dir, filename); 92 } 93 } 94 } 95 /++ 96 Returns a nice "default" list of config files. arg0 should be the first element 97 of the args array passed to main(). The optional name parameter should be the _name of the config file, 98 minus any extension. It defaults to the executable's _name, minus any directory 99 or extension. 100 101 On Windows, the returned filenames are: 102 $(UL 103 $(LI name.ini alongside the .exe) 104 $(LI %HOMEDRIVE%%HOMEPATH%\name.ini) 105 $(LI .\name.ini) 106 ) 107 108 On all other platforms (e.g. Linux), the returned filenames are: 109 $(UL 110 $(LI /etc/name.conf) 111 $(LI name.ini alongside the binary) 112 $(LI ~/.namerc) 113 $(LI ./name.conf) 114 ) 115 +/ 116 char[][] config_files(char[] arg0, char[] name=null) { 117 if (name is null) name = get_program_name(arg0); 118 version (Windows) { 119 name ~= ".ini"; 120 return [ 121 same_dir(arg0, name), 122 join(env("HOMEDRIVE")~env("HOMEPATH"), name), 123 name 124 ]; 125 } else { 126 return [ 127 "/etc/"~name~".conf", 128 same_dir(arg0, name~".ini"), 129 "~/."~name~"rc", 130 name~".conf" 131 ]; 132 } 133 } 134 135 /++ 136 The ConfigParser class represents a config file. 137 +/ 57 138 class ConfigParser { 58 139 char[][char[]][char[]] config; … … 61 142 } 62 143 144 /++ 145 Returns a list of the _sections in the parser, including the global 146 section. ("General" by default.) 147 +/ 63 148 char[][] sections() { 64 149 return this.config.keys; 65 150 } 151 /++ 152 Adds a section named name to the parser. 153 154 Throws: DuplicateSectionError if the section already exists. 155 +/ 66 156 void add_section(char[] name) { 67 157 auto section = name in config; … … 72 162 } 73 163 } 164 /// Requests if a section exists in the parser. 74 165 bool has_section(char[] name) { 75 166 return name in config !is null; 76 167 } 77 168 private void no_section(char[] section) { 78 throw new SectionNotFoundError("Section '"~section~"' not found."); 79 } 80 private void no_option(char[] option) { 81 throw new OptionNotFoundError("Option '"~option~"' not found."); 82 } 169 throw new SectionNotFoundError("Configuration section '"~section~"' not found."); 170 } 171 private void no_option(char[] section, char[] option) { 172 throw new OptionNotFoundError("Configuration option '"~option~"' in section '"~section~"' not found."); 173 } 174 /++ 175 Returns a list of _options in the given section. 176 177 Throws: SectionNotFoundError 178 +/ 83 179 char[][] options(char[] section=GLOBAL_SECTION) { 84 180 auto s = section in config; … … 86 182 return s.keys; 87 183 } 184 /++ 185 Returns a list of _values in the given section. 186 187 Throws: SectionNotFoundError 188 +/ 88 189 char[][] values(char[] section=GLOBAL_SECTION) { 89 190 auto s = section in config; … … 91 192 return s.values; 92 193 } 194 /++ 195 Returns whether the given option exists in the given section. This method 196 (and all of the others in this form) will set option to section, and 197 section to the global _section, when only one argument is provided. (In 198 other words, providing only one argument will check for options in the 199 global _section.) This will silently return false if the section does not 200 exist. 201 +/ 93 202 bool has_option(char[] section, char[] option=null) { 94 203 if (option is null) { … … 102 211 else return true; 103 212 } 213 /++ 214 Reads a series of files into the parser, in the order provided. Files that 215 don't exist will be silently skipped. Options specified in later files will 216 override those specified in earlier files. 217 218 Returns: A list of files _read in. 219 Throws: ConfigParseError if a file contains errors. 220 +/ 104 221 char[][] read(char[][] filenames...) { 105 222 char[][] files_read; … … 107 224 if (!exists(filename)) continue; 108 225 files_read ~= filename; 109 this.read(new BufferedFile(filename), filename); 226 Stream s = new BufferedFile(filename); 227 this.read(s, filename); 228 delete s; 110 229 } 111 230 return files_read; 112 231 } 232 /++ 233 Reads in any Stream as a config _file. 234 235 Params: 236 filename = An optional argument for clarifying error output. 237 Throws: ConfigParseError if the stream contains errors. 238 +/ 113 239 void read(Stream file, char[] filename="") { 114 240 if (filename.length != 0) filename ~= " "; … … 139 265 } 140 266 } 267 /++ 268 Retrieves the value of an option in the given section. If only one argument 269 is supplied, it is treated as an _option in the global _section. 270 271 Throws: 272 SectionNotFoundError if the section doesn't exist. 273 OptionNotFoundError if the option doesn't exist. 274 +/ 141 275 char[] opIndex(char[] section, char[] option=null) { 142 276 if (option is null) { … … 147 281 if (s is null) no_section(section); 148 282 auto o = option in (*s); 149 if (o is null) no_option( option);283 if (o is null) no_option(section, option); 150 284 return *o; 151 285 } 286 /// An alias of opIndex. 152 287 alias opIndex get; 288 /++ 289 Retrieves the value of an option in the given section in a manner identical 290 to opIndex, and attempts to convert it to an integer. 291 292 Throws: ConfigConvertError if the value cannot be converted. 293 +/ 153 294 int getint(char[] section, char[] option=null) { 154 295 char[] o = this[section, option]; … … 156 297 return toInt(o); 157 298 } catch(ConvError e) { 158 throw new ConfigConvertError("Could not convert '"~o~"' to an integer."); 159 } 160 } 299 throw new ConfigConvertError("Could not convert '"~o~"' to an integer in option "~section~"."~option~"."); 300 } 301 } 302 /++ 303 Retrieves the value of an option in the given section in a manner identical 304 to opIndex, and attempts to convert it to a real. 305 306 Throws: ConfigConvertError if the value cannot be converted. 307 +/ 161 308 real getreal(char[] section, char[] option=null) { 162 309 char[] o = this[section, option]; … … 167 314 } 168 315 } 316 /++ 317 Retrieves the value of an option in the given section in a manner identical 318 to opIndex, and attempts to convert it to a boolean. The value is first 319 converted to lower-case. The accepted values for true are "yes," "_true," 320 "on," and "1." The accepted values for false are "no," "_false," "off," and 321 "0." 322 323 Throws: ConfigConvertError if the value cannot be converted. 324 +/ 169 325 bool getbool(char[] section, char[] option=null) { 170 326 char[] o = tolower(this[section, option]); … … 178 334 } 179 335 } 336 /++ 337 Stores value to option in section. If option is not supplied, section is 338 treated like an _option in the global _section. 339 340 Throws: SectionNotFoundError if section does not exist. 341 +/ 180 342 void opIndexAssign(char[] value, char[] section, char[] option=null) { 181 343 if (option is null) { … … 187 349 (*s)[option] = value; 188 350 } 351 /++ 352 Calls opIndexAssign after converting value to a string. 353 +/ 189 354 void opIndexAssign(int value, char[] section, char[] option=null) { 190 355 this.opIndexAssign(.toString(value), section, option); 191 356 } 357 /++ 358 Calls opIndexAssign after converting value to a string. 359 +/ 360 void opIndexAssign(real value, char[] section, char[] option=null) { 361 this.opIndexAssign(.toString(value), section, option); 362 } 363 /++ 364 Calls opIndexAssign after converting value to a string. 365 +/ 192 366 void opIndexAssign(bool value, char[] section, char[] option=null) { 193 367 this.opIndexAssign(.toString(value), section, option); 194 368 } 369 /++ 370 Removes the specified option in section. Silently does nothing if option 371 does not exist. 372 373 Throws: SectionNotFoundError if the section does not exist. 374 +/ 195 375 void remove_option(char[] section, char[] option=null) { 196 376 if (option is null) { … … 202 382 (*s).remove(option); 203 383 } 384 /++ 385 Removes section. Silently does nothing if section does not exist. 386 +/ 204 387 void remove_section(char[] section) { 205 388 this.config.remove(section); 206 389 } 390 /++ 391 Writes the config _file to file. Note that ConfigParser does not preserve 392 the ordering of options or sections, although it will always write the 393 global section first. 394 +/ 207 395 void write(Stream file) { 208 396 auto global = config[GLOBAL_SECTION]; … … 223 411 file.flush(); 224 412 } 413 /++ 414 Writes the config file to the file specified by filename. If the file 415 already exists, it is erased and written over. If it does not exist, it is 416 created. 417 +/ 225 418 void write(char[] filename) { 226 this.write(new BufferedFile(filename, FileMode.OutNew)); 227 } 228 } 229 419 Stream s = new BufferedFile(filename, FileMode.OutNew); 420 this.write(s); 421 s.close(); 422 delete s; 423 } 424 } 425 misc/configtest.d
r94 r95 4 4 import configparse; 5 5 6 void main( ) {6 void main(char[][] args) { 7 7 auto config = new ConfigParser; 8 8 config.read("test.ini"); … … 17 17 } 18 18 config.write("test2.ini"); 19 writefln(config_files(args[0], "foo")); 19 20 } 20 21
