1 /* 2 * Some portions copyright (c) 1994-1995 by Symantec 3 * Copyright (c) 1999-2016 by Digital Mars 4 * All Rights Reserved 5 * http://www.digitalmars.com 6 * Written by Walter Bright 7 * 8 * This source file is made available for personal use 9 * only. The license is in backendlicense.txt 10 * For any other uses, please contact Digital Mars. 11 */ 12 13 module ddmd.dinifile; 14 15 import core.stdc.ctype; 16 import core.stdc..string; 17 import core.sys.posix.stdlib; 18 import core.sys.windows.windows; 19 20 import ddmd.errors; 21 import ddmd.globals; 22 import ddmd.root.filename; 23 import ddmd.root.outbuffer; 24 import ddmd.root.port; 25 import ddmd.root.stringtable; 26 27 version (Windows) extern (C) int putenv(const char*); 28 private enum LOG = false; 29 30 /***************************** 31 * Find the config file 32 * Params: 33 * argv0 = program name (argv[0]) 34 * inifile = .ini file name 35 * Returns: 36 * file path of the config file or NULL 37 * Note: this is a memory leak 38 */ 39 const(char)* findConfFile(const(char)* argv0, const(char)* inifile) 40 { 41 static if (LOG) 42 { 43 printf("findinifile(argv0 = '%s', inifile = '%s')\n", argv0, inifile); 44 } 45 if (FileName.absolute(inifile)) 46 return inifile; 47 if (FileName.exists(inifile)) 48 return inifile; 49 /* Look for inifile in the following sequence of places: 50 * o current directory 51 * o home directory 52 * o exe directory (windows) 53 * o directory off of argv0 54 * o SYSCONFDIR=/etc (non-windows) 55 */ 56 auto filename = FileName.combine(getenv("HOME"), inifile); 57 if (FileName.exists(filename)) 58 return filename; 59 version (Windows) 60 { 61 // This fix by Tim Matthews 62 char[MAX_PATH + 1] resolved_name; 63 if (GetModuleFileNameA(null, resolved_name.ptr, MAX_PATH + 1) && FileName.exists(resolved_name.ptr)) 64 { 65 filename = FileName.replaceName(resolved_name.ptr, inifile); 66 if (FileName.exists(filename)) 67 return filename; 68 } 69 } 70 filename = FileName.replaceName(argv0, inifile); 71 if (FileName.exists(filename)) 72 return filename; 73 version (Posix) 74 { 75 // Search PATH for argv0 76 auto p = getenv("PATH"); 77 static if (LOG) 78 { 79 printf("\tPATH='%s'\n", p); 80 } 81 auto paths = FileName.splitPath(p); 82 auto abspath = FileName.searchPath(paths, argv0, false); 83 if (abspath) 84 { 85 auto absname = FileName.replaceName(abspath, inifile); 86 if (FileName.exists(absname)) 87 return absname; 88 } 89 // Resolve symbolic links 90 filename = FileName.canonicalName(abspath ? abspath : argv0); 91 if (filename) 92 { 93 filename = FileName.replaceName(filename, inifile); 94 if (FileName.exists(filename)) 95 return filename; 96 } 97 // Search SYSCONFDIR=/etc for inifile 98 filename = FileName.combine(import("SYSCONFDIR.imp"), inifile); 99 } 100 return filename; 101 } 102 103 /********************************** 104 * Read from environment, looking for cached value first. 105 * Params: 106 * environment = cached copy of the environment 107 * name = name to look for 108 * Returns: 109 * environment value corresponding to name 110 */ 111 const(char)* readFromEnv(StringTable* environment, const(char)* name) 112 { 113 const len = strlen(name); 114 auto sv = environment.lookup(name, len); 115 if (sv) 116 return cast(const(char)*)sv.ptrvalue; // get cached value 117 return getenv(name); 118 } 119 120 /********************************* 121 * Write to our copy of the environment, not the real environment 122 */ 123 private bool writeToEnv(StringTable* environment, char* nameEqValue) 124 { 125 auto p = strchr(nameEqValue, '='); 126 if (!p) 127 return false; 128 auto sv = environment.update(nameEqValue, p - nameEqValue); 129 sv.ptrvalue = cast(void*)(p + 1); 130 return true; 131 } 132 133 /************************************ 134 * Update real enviroment with our copy. 135 * Params: 136 * environment = our copy of the environment 137 */ 138 void updateRealEnvironment(StringTable* environment) 139 { 140 extern (C++) static int envput(const(StringValue)* sv) 141 { 142 const name = sv.toDchars(); 143 const namelen = strlen(name); 144 const value = cast(const(char)*)sv.ptrvalue; 145 const valuelen = strlen(value); 146 auto s = cast(char*)malloc(namelen + 1 + valuelen + 1); 147 assert(s); 148 memcpy(s, name, namelen); 149 s[namelen] = '='; 150 memcpy(s + namelen + 1, value, valuelen); 151 s[namelen + 1 + valuelen] = 0; 152 //printf("envput('%s')\n", s); 153 putenv(s); 154 return 0; // do all of them 155 } 156 157 environment.apply(&envput); 158 } 159 160 /***************************** 161 * Read and analyze .ini file. 162 * Write the entries into environment as 163 * well as any entries in one of the specified section(s). 164 * 165 * Params: 166 * environment = our own cache of the program environment 167 * filename = name of the file being parsed 168 * path = what @P will expand to 169 * buffer[len] = contents of configuration file 170 * sections[] = section names 171 */ 172 void parseConfFile(StringTable* environment, const(char)* filename, const(char)* path, size_t length, ubyte* buffer, Strings* sections) 173 { 174 /******************** 175 * Skip spaces. 176 */ 177 static inout(char)* skipspace(inout(char)* p) 178 { 179 while (isspace(*p)) 180 p++; 181 return p; 182 } 183 184 // Parse into lines 185 bool envsection = true; // default is to read 186 OutBuffer buf; 187 bool eof = false; 188 int lineNum = 0; 189 for (size_t i = 0; i < length && !eof; i++) 190 { 191 Lstart: 192 size_t linestart = i; 193 for (; i < length; i++) 194 { 195 switch (buffer[i]) 196 { 197 case '\r': 198 break; 199 case '\n': 200 // Skip if it was preceded by '\r' 201 if (i && buffer[i - 1] == '\r') 202 { 203 i++; 204 goto Lstart; 205 } 206 break; 207 case 0: 208 case 0x1A: 209 eof = true; 210 break; 211 default: 212 continue; 213 } 214 break; 215 } 216 ++lineNum; 217 buf.reset(); 218 // First, expand the macros. 219 // Macros are bracketed by % characters. 220 Kloop: 221 for (size_t k = 0; k < i - linestart; ++k) 222 { 223 // The line is buffer[linestart..i] 224 char* line = cast(char*)&buffer[linestart]; 225 if (line[k] == '%') 226 { 227 foreach (size_t j; k + 1 .. i - linestart) 228 { 229 if (line[j] != '%') 230 continue; 231 if (j - k == 3 && Port.memicmp(&line[k + 1], "@P", 2) == 0) 232 { 233 // %@P% is special meaning the path to the .ini file 234 auto p = path; 235 if (!*p) 236 p = "."; 237 buf.writestring(p); 238 } 239 else 240 { 241 auto len2 = j - k; 242 auto p = cast(char*)malloc(len2); 243 len2--; 244 memcpy(p, &line[k + 1], len2); 245 p[len2] = 0; 246 Port.strupr(p); 247 const penv = readFromEnv(environment, p); 248 if (penv) 249 buf.writestring(penv); 250 free(p); 251 } 252 k = j; 253 continue Kloop; 254 } 255 } 256 buf.writeByte(line[k]); 257 } 258 259 // Remove trailing spaces 260 const slice = buf.peekSlice(); 261 auto slicelen = slice.length; 262 while (slicelen && isspace(slice[slicelen - 1])) 263 --slicelen; 264 buf.setsize(slicelen); 265 266 auto p = buf.peekString(); 267 // The expanded line is in p. 268 // Now parse it for meaning. 269 p = skipspace(p); 270 switch (*p) 271 { 272 case ';': 273 // comment 274 case 0: 275 // blank 276 break; 277 case '[': 278 // look for [Environment] 279 p = skipspace(p + 1); 280 char* pn; 281 for (pn = p; isalnum(*pn); pn++) 282 { 283 } 284 if (*skipspace(pn) != ']') 285 { 286 // malformed [sectionname], so just say we're not in a section 287 envsection = false; 288 break; 289 } 290 /* Seach sectionnamev[] for p..pn and set envsection to true if it's there 291 */ 292 for (size_t j = 0; 1; ++j) 293 { 294 if (j == sections.dim) 295 { 296 // Didn't find it 297 envsection = false; 298 break; 299 } 300 const sectionname = (*sections)[j]; 301 const len = strlen(sectionname); 302 if (pn - p == len && Port.memicmp(p, sectionname, len) == 0) 303 { 304 envsection = true; 305 break; 306 } 307 } 308 break; 309 default: 310 if (envsection) 311 { 312 auto pn = p; 313 // Convert name to upper case; 314 // remove spaces bracketing = 315 for (; *p; p++) 316 { 317 if (islower(*p)) 318 *p &= ~0x20; 319 else if (isspace(*p)) 320 { 321 memmove(p, p + 1, strlen(p)); 322 p--; 323 } 324 else if (p[0] == '?' && p[1] == '=') 325 { 326 *p = '\0'; 327 if (readFromEnv(environment, pn)) 328 { 329 pn = null; 330 break; 331 } 332 // remove the '?' and resume parsing starting from 333 // '=' again so the regular variable format is 334 // parsed 335 memmove(p, p + 1, strlen(p + 1) + 1); 336 p--; 337 } 338 else if (*p == '=') 339 { 340 p++; 341 while (isspace(*p)) 342 memmove(p, p + 1, strlen(p)); 343 break; 344 } 345 } 346 if (pn) 347 { 348 if (!writeToEnv(environment, strdup(pn))) 349 { 350 error(Loc(filename, lineNum, 0), "Use 'NAME=value' syntax, not '%s'", pn); 351 fatal(); 352 } 353 static if (LOG) 354 { 355 printf("\tputenv('%s')\n", pn); 356 //printf("getenv(\"TEST\") = '%s'\n",getenv("TEST")); 357 } 358 } 359 } 360 break; 361 } 362 } 363 }