blob: a8c6791545e0ed1a2d7a0221f4af26c245bf820a [file] [log] [blame]
Reid Spencercbabe7f2004-08-19 21:17:53 +00001//===- Configuration.cpp - Configuration Data Mgmt --------------*- C++ -*-===//
Reid Spencer2594c9a2004-08-13 20:21:22 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file was developed by Reid Spencer and is distributed under the
6// University of Illinois Open Source License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the parsing of configuration files for the LLVM Compiler
11// Driver (llvmc).
12//
13//===------------------------------------------------------------------------===
14
Reid Spencerf51a87c2004-08-19 21:52:49 +000015#include "Configuration.h"
Reid Spencer68fb37a2004-08-14 09:37:15 +000016#include "ConfigLexer.h"
Reid Spencer2594c9a2004-08-13 20:21:22 +000017#include "CompilerDriver.h"
Reid Spencerb38e4052004-08-20 09:24:07 +000018#include "Config/config.h"
Reid Spencerbae68252004-08-19 04:49:47 +000019#include "Support/CommandLine.h"
Reid Spencer2594c9a2004-08-13 20:21:22 +000020#include "Support/StringExtras.h"
21#include <iostream>
Reid Spencer68fb37a2004-08-14 09:37:15 +000022#include <fstream>
Reid Spencer2594c9a2004-08-13 20:21:22 +000023
24using namespace llvm;
25
Reid Spencerb38e4052004-08-20 09:24:07 +000026namespace sys {
27 // From CompilerDriver.cpp (for now)
Reid Spencerf384db32004-08-24 14:03:23 +000028 extern bool FileIsReadable(const std::string& fname);
Reid Spencerb38e4052004-08-20 09:24:07 +000029}
30
Reid Spencer68fb37a2004-08-14 09:37:15 +000031namespace llvm {
Reid Spencerbae68252004-08-19 04:49:47 +000032 ConfigLexerInfo ConfigLexerState;
Reid Spencer68fb37a2004-08-14 09:37:15 +000033 InputProvider* ConfigLexerInput = 0;
Reid Spencer68fb37a2004-08-14 09:37:15 +000034
35 InputProvider::~InputProvider() {}
36 void InputProvider::error(const std::string& msg) {
Reid Spencerbae68252004-08-19 04:49:47 +000037 std::cerr << name << ":" << ConfigLexerState.lineNum << ": Error: " <<
38 msg << "\n";
Reid Spencer68fb37a2004-08-14 09:37:15 +000039 errCount++;
40 }
41
42 void InputProvider::checkErrors() {
43 if (errCount > 0) {
44 std::cerr << name << " had " << errCount << " errors. Terminating.\n";
45 exit(errCount);
46 }
47 }
48
49}
50
Reid Spencer2594c9a2004-08-13 20:21:22 +000051namespace {
52
Reid Spencer68fb37a2004-08-14 09:37:15 +000053 class FileInputProvider : public InputProvider {
54 public:
55 FileInputProvider(const std::string & fname)
56 : InputProvider(fname)
57 , F(fname.c_str()) {
58 ConfigLexerInput = this;
59 }
60 virtual ~FileInputProvider() { F.close(); ConfigLexerInput = 0; }
61 virtual unsigned read(char *buffer, unsigned max_size) {
62 if (F.good()) {
63 F.read(buffer,max_size);
64 if ( F.gcount() ) return F.gcount() - 1;
65 }
66 return 0;
67 }
Reid Spencer2594c9a2004-08-13 20:21:22 +000068
Reid Spencer68fb37a2004-08-14 09:37:15 +000069 bool okay() { return F.good(); }
70 private:
71 std::ifstream F;
72 };
Reid Spencer2594c9a2004-08-13 20:21:22 +000073
Reid Spencerbae68252004-08-19 04:49:47 +000074 cl::opt<bool> DumpTokens("dump-tokens", cl::Optional, cl::Hidden, cl::init(false),
75 cl::desc("Dump lexical tokens (debug use only)."));
76
Reid Spencerbf437722004-08-15 08:19:46 +000077 struct Parser
Reid Spencer68fb37a2004-08-14 09:37:15 +000078 {
Reid Spencerbae68252004-08-19 04:49:47 +000079 Parser() {
80 token = EOFTOK;
81 provider = 0;
82 confDat = 0;
83 ConfigLexerState.lineNum = 1;
84 ConfigLexerState.in_value = false;
85 ConfigLexerState.StringVal.clear();
86 ConfigLexerState.IntegerVal = 0;
87 };
88
Reid Spencerbf437722004-08-15 08:19:46 +000089 ConfigLexerTokens token;
Reid Spencer68fb37a2004-08-14 09:37:15 +000090 InputProvider* provider;
91 CompilerDriver::ConfigData* confDat;
Reid Spencer2594c9a2004-08-13 20:21:22 +000092
Reid Spencer52c2dc12004-08-29 19:26:56 +000093 inline int next() {
Reid Spencerbae68252004-08-19 04:49:47 +000094 token = Configlex();
95 if (DumpTokens)
96 std::cerr << token << "\n";
97 return token;
98 }
Reid Spencer68fb37a2004-08-14 09:37:15 +000099
Reid Spencer52c2dc12004-08-29 19:26:56 +0000100 inline bool next_is_real() {
Reid Spencerbae68252004-08-19 04:49:47 +0000101 next();
Reid Spencer68fb37a2004-08-14 09:37:15 +0000102 return (token != EOLTOK) && (token != ERRORTOK) && (token != 0);
Reid Spencer2594c9a2004-08-13 20:21:22 +0000103 }
104
Reid Spencer52c2dc12004-08-29 19:26:56 +0000105 inline void eatLineRemnant() {
Reid Spencer68fb37a2004-08-14 09:37:15 +0000106 while (next_is_real()) ;
Reid Spencer2594c9a2004-08-13 20:21:22 +0000107 }
108
Reid Spencer68fb37a2004-08-14 09:37:15 +0000109 void error(const std::string& msg, bool skip = true) {
110 provider->error(msg);
111 if (skip)
112 eatLineRemnant();
113 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000114
Reid Spencer68fb37a2004-08-14 09:37:15 +0000115 std::string parseName() {
116 std::string result;
117 if (next() == EQUALS) {
118 while (next_is_real()) {
119 switch (token ) {
120 case STRING :
121 case OPTION :
Reid Spencerbae68252004-08-19 04:49:47 +0000122 result += ConfigLexerState.StringVal + " ";
Reid Spencer68fb37a2004-08-14 09:37:15 +0000123 break;
124 default:
125 error("Invalid name");
126 break;
127 }
128 }
129 if (result.empty())
130 error("Name exepected");
131 else
132 result.erase(result.size()-1,1);
133 } else
134 error("= expected");
135 return result;
136 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000137
Reid Spencer68fb37a2004-08-14 09:37:15 +0000138 bool parseBoolean() {
139 bool result = true;
140 if (next() == EQUALS) {
141 if (next() == FALSETOK) {
142 result = false;
143 } else if (token != TRUETOK) {
144 error("Expecting boolean value");
145 return false;
146 }
147 if (next() != EOLTOK && token != 0) {
148 error("Extraneous tokens after boolean");
149 }
150 }
151 else
152 error("Expecting '='");
153 return result;
154 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000155
Reid Spencerbae68252004-08-19 04:49:47 +0000156 bool parseSubstitution(CompilerDriver::StringVector& optList) {
157 switch (token) {
Reid Spencerf384db32004-08-24 14:03:23 +0000158 case ARGS_SUBST: optList.push_back("%args%"); break;
Reid Spencer53aa7932004-08-20 22:53:11 +0000159 case IN_SUBST: optList.push_back("%in%"); break;
160 case OUT_SUBST: optList.push_back("%out%"); break;
161 case TIME_SUBST: optList.push_back("%time%"); break;
162 case STATS_SUBST: optList.push_back("%stats%"); break;
163 case OPT_SUBST: optList.push_back("%opt%"); break;
164 case TARGET_SUBST: optList.push_back("%target%"); break;
Reid Spencer52c2dc12004-08-29 19:26:56 +0000165 case FORCE_SUBST: optList.push_back("%force%"); break;
166 case VERBOSE_SUBST: optList.push_back("%verbose%"); break;
Reid Spencerbae68252004-08-19 04:49:47 +0000167 default:
168 return false;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000169 }
Reid Spencerbae68252004-08-19 04:49:47 +0000170 return true;
171 }
172
173 void parseOptionList(CompilerDriver::StringVector& optList ) {
174 if (next() == EQUALS) {
175 while (next_is_real()) {
176 if (token == STRING || token == OPTION)
177 optList.push_back(ConfigLexerState.StringVal);
178 else if (!parseSubstitution(optList)) {
179 error("Expecting a program argument or substitution", false);
180 break;
181 }
182 }
183 } else
184 error("Expecting '='");
Reid Spencerbf437722004-08-15 08:19:46 +0000185 }
186
Reid Spencer59a745a2004-08-22 18:03:25 +0000187 void parseVersion() {
188 if (next() == EQUALS) {
189 while (next_is_real()) {
190 if (token == STRING || token == OPTION)
191 confDat->version = ConfigLexerState.StringVal;
192 else
193 error("Expecting a version string");
194 }
195 } else
196 error("Expecting '='");
197 }
198
Reid Spencerbf437722004-08-15 08:19:46 +0000199 void parseLang() {
200 switch (next() ) {
201 case NAME:
202 confDat->langName = parseName();
203 break;
204 case OPT1:
205 parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]);
206 break;
207 case OPT2:
208 parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]);
209 break;
210 case OPT3:
211 parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]);
212 break;
213 case OPT4:
214 parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]);
215 break;
216 case OPT5:
217 parseOptionList(
218 confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]);
219 break;
220 default:
221 error("Expecting 'name' or 'optN' after 'lang.'");
222 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000223 }
224 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000225
Reid Spencer68fb37a2004-08-14 09:37:15 +0000226 void parseCommand(CompilerDriver::Action& action) {
227 if (next() == EQUALS) {
Reid Spencerbf437722004-08-15 08:19:46 +0000228 if (next() == EOLTOK) {
Reid Spencer68fb37a2004-08-14 09:37:15 +0000229 // no value (valid)
230 action.program.clear();
231 action.args.clear();
Reid Spencer68fb37a2004-08-14 09:37:15 +0000232 } else {
233 if (token == STRING || token == OPTION) {
Reid Spencer52c2dc12004-08-29 19:26:56 +0000234 action.program.set_file(ConfigLexerState.StringVal);
Reid Spencer2594c9a2004-08-13 20:21:22 +0000235 } else {
Reid Spencer68fb37a2004-08-14 09:37:15 +0000236 error("Expecting a program name");
237 }
238 while (next_is_real()) {
Reid Spencerbf437722004-08-15 08:19:46 +0000239 if (token == STRING || token == OPTION) {
Reid Spencerbae68252004-08-19 04:49:47 +0000240 action.args.push_back(ConfigLexerState.StringVal);
241 } else if (!parseSubstitution(action.args)) {
242 error("Expecting a program argument or substitution", false);
Reid Spencerbf437722004-08-15 08:19:46 +0000243 break;
244 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000245 }
246 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000247 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000248 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000249
Reid Spencerbf437722004-08-15 08:19:46 +0000250 void parsePreprocessor() {
251 switch (next()) {
252 case COMMAND:
253 parseCommand(confDat->PreProcessor);
254 break;
255 case REQUIRED:
256 if (parseBoolean())
257 confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG);
258 else
259 confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
260 break;
261 default:
Reid Spencerf384db32004-08-24 14:03:23 +0000262 error("Expecting 'command' or 'required' but found '" +
263 ConfigLexerState.StringVal);
Reid Spencerbf437722004-08-15 08:19:46 +0000264 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000265 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000266 }
267
Reid Spencerf384db32004-08-24 14:03:23 +0000268 bool parseOutputFlag() {
269 if (next() == EQUALS) {
270 if (next() == ASSEMBLY) {
271 return true;
272 } else if (token == BYTECODE) {
273 return false;
274 } else {
275 error("Expecting output type value");
276 return false;
277 }
278 if (next() != EOLTOK && token != 0) {
279 error("Extraneous tokens after output value");
280 }
281 }
282 else
283 error("Expecting '='");
284 return false;
285 }
286
Reid Spencer68fb37a2004-08-14 09:37:15 +0000287 void parseTranslator() {
Reid Spencerbf437722004-08-15 08:19:46 +0000288 switch (next()) {
289 case COMMAND:
290 parseCommand(confDat->Translator);
291 break;
292 case REQUIRED:
293 if (parseBoolean())
294 confDat->Translator.set(CompilerDriver::REQUIRED_FLAG);
295 else
296 confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG);
297 break;
298 case PREPROCESSES:
299 if (parseBoolean())
300 confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
301 else
302 confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
303 break;
Reid Spencerf384db32004-08-24 14:03:23 +0000304 case OUTPUT:
305 if (parseOutputFlag())
Reid Spencerbae68252004-08-19 04:49:47 +0000306 confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000307 else
Reid Spencerbae68252004-08-19 04:49:47 +0000308 confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000309 break;
Reid Spencerbae68252004-08-19 04:49:47 +0000310
Reid Spencerbf437722004-08-15 08:19:46 +0000311 default:
Reid Spencerf384db32004-08-24 14:03:23 +0000312 error("Expecting 'command', 'required', 'preprocesses', or "
313 "'output' but found '" + ConfigLexerState.StringVal +
314 "' instead");
Reid Spencerbf437722004-08-15 08:19:46 +0000315 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000316 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000317 }
318
319 void parseOptimizer() {
Reid Spencerbf437722004-08-15 08:19:46 +0000320 switch (next()) {
321 case COMMAND:
322 parseCommand(confDat->Optimizer);
323 break;
Reid Spencerbae68252004-08-19 04:49:47 +0000324 case PREPROCESSES:
325 if (parseBoolean())
326 confDat->Optimizer.set(CompilerDriver::PREPROCESSES_FLAG);
327 else
328 confDat->Optimizer.clear(CompilerDriver::PREPROCESSES_FLAG);
329 break;
330 case TRANSLATES:
331 if (parseBoolean())
332 confDat->Optimizer.set(CompilerDriver::TRANSLATES_FLAG);
333 else
334 confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG);
335 break;
Reid Spencerf384db32004-08-24 14:03:23 +0000336 case REQUIRED:
Reid Spencerbf437722004-08-15 08:19:46 +0000337 if (parseBoolean())
Reid Spencerf384db32004-08-24 14:03:23 +0000338 confDat->Optimizer.set(CompilerDriver::REQUIRED_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000339 else
Reid Spencerf384db32004-08-24 14:03:23 +0000340 confDat->Optimizer.clear(CompilerDriver::REQUIRED_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000341 break;
Reid Spencerf384db32004-08-24 14:03:23 +0000342 case OUTPUT:
343 if (parseOutputFlag())
Reid Spencerbae68252004-08-19 04:49:47 +0000344 confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000345 else
Reid Spencerbae68252004-08-19 04:49:47 +0000346 confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000347 break;
348 default:
Reid Spencerf384db32004-08-24 14:03:23 +0000349 error(std::string("Expecting 'command', 'preprocesses', ") +
350 "'translates' or 'output' but found '" +
351 ConfigLexerState.StringVal + "' instead");
Reid Spencerbf437722004-08-15 08:19:46 +0000352 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000353 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000354 }
355
356 void parseAssembler() {
Reid Spencerbf437722004-08-15 08:19:46 +0000357 switch(next()) {
358 case COMMAND:
359 parseCommand(confDat->Assembler);
360 break;
361 default:
362 error("Expecting 'command'");
363 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000364 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000365 }
366
367 void parseLinker() {
Reid Spencerbf437722004-08-15 08:19:46 +0000368 switch(next()) {
Reid Spencerf384db32004-08-24 14:03:23 +0000369 case LIBS:
370 break; //FIXME
371 case LIBPATHS:
372 break; //FIXME
Reid Spencerbf437722004-08-15 08:19:46 +0000373 default:
Reid Spencerf384db32004-08-24 14:03:23 +0000374 error("Expecting 'libs' or 'libpaths'");
Reid Spencerbf437722004-08-15 08:19:46 +0000375 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000376 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000377 }
378
379 void parseAssignment() {
380 switch (token) {
Reid Spencer59a745a2004-08-22 18:03:25 +0000381 case VERSION: parseVersion(); break;
Reid Spencerbf437722004-08-15 08:19:46 +0000382 case LANG: parseLang(); break;
383 case PREPROCESSOR: parsePreprocessor(); break;
384 case TRANSLATOR: parseTranslator(); break;
385 case OPTIMIZER: parseOptimizer(); break;
386 case ASSEMBLER: parseAssembler(); break;
387 case LINKER: parseLinker(); break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000388 case EOLTOK: break; // just ignore
389 case ERRORTOK:
390 default:
Reid Spencerbf437722004-08-15 08:19:46 +0000391 error("Invalid top level configuration item");
392 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000393 }
394 }
395
396 void parseFile() {
Reid Spencerbf437722004-08-15 08:19:46 +0000397 while ( next() != EOFTOK ) {
398 if (token == ERRORTOK)
399 error("Invalid token");
400 else if (token != EOLTOK)
401 parseAssignment();
Reid Spencer68fb37a2004-08-14 09:37:15 +0000402 }
403 provider->checkErrors();
404 }
405 };
406
407 void
408 ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
Reid Spencerbf437722004-08-15 08:19:46 +0000409 Parser p;
410 p.token = EOFTOK;
411 p.provider = &provider;
412 p.confDat = &confDat;
Reid Spencerbf437722004-08-15 08:19:46 +0000413 p.parseFile();
Reid Spencer2594c9a2004-08-13 20:21:22 +0000414 }
415}
416
417CompilerDriver::ConfigData*
Reid Spencer68fb37a2004-08-14 09:37:15 +0000418LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
419 CompilerDriver::ConfigData* result = 0;
Reid Spencer52c2dc12004-08-29 19:26:56 +0000420 sys::Path confFile;
421 if (configDir.is_empty()) {
Reid Spencerb38e4052004-08-20 09:24:07 +0000422 // Try the environment variable
423 const char* conf = getenv("LLVM_CONFIG_DIR");
424 if (conf) {
Reid Spencer52c2dc12004-08-29 19:26:56 +0000425 confFile.set_directory(conf);
426 confFile.append_file(ftype);
427 if (!confFile.readable())
Reid Spencerb38e4052004-08-20 09:24:07 +0000428 throw "Configuration file for '" + ftype + "' is not available.";
429 } else {
430 // Try the user's home directory
Reid Spencer52c2dc12004-08-29 19:26:56 +0000431 confFile = sys::Path::GetUserHomeDirectory();
432 if (!confFile.is_empty()) {
433 confFile.append_directory(".llvm");
434 confFile.append_directory("etc");
435 confFile.append_file(ftype);
436 if (!confFile.readable())
437 confFile.clear();
438 }
439 if (!confFile.is_empty()) {
440 // Okay, try the LLVM installation directory
441 confFile = sys::Path::GetLLVMConfigDir();
442 confFile.append_file(ftype);
443 if (!confFile.readable()) {
444 // Okay, try the "standard" place
445 confFile = sys::Path::GetLLVMDefaultConfigDir();
446 confFile.append_file(ftype);
447 if (!confFile.readable()) {
448 throw "Configuration file for '" + ftype + "' is not available.";
Reid Spencerb38e4052004-08-20 09:24:07 +0000449 }
450 }
451 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000452 }
453 } else {
Reid Spencer52c2dc12004-08-29 19:26:56 +0000454 confFile = configDir;
455 confFile.append_file(ftype);
456 if (!confFile.readable())
Reid Spencerb38e4052004-08-20 09:24:07 +0000457 throw "Configuration file for '" + ftype + "' is not available.";
Reid Spencer2594c9a2004-08-13 20:21:22 +0000458 }
Reid Spencer52c2dc12004-08-29 19:26:56 +0000459 FileInputProvider fip( confFile.get() );
Reid Spencerb38e4052004-08-20 09:24:07 +0000460 if (!fip.okay()) {
461 throw "Configuration file for '" + ftype + "' is not available.";
462 }
463 result = new CompilerDriver::ConfigData();
464 ParseConfigData(fip,*result);
Reid Spencer68fb37a2004-08-14 09:37:15 +0000465 return result;
Reid Spencer2594c9a2004-08-13 20:21:22 +0000466}
467
Reid Spencer2594c9a2004-08-13 20:21:22 +0000468LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
469{
470 ConfigDataMap::iterator cIt = Configurations.begin();
471 while (cIt != Configurations.end()) {
472 CompilerDriver::ConfigData* cd = cIt->second;
473 ++cIt;
474 delete cd;
475 }
476 Configurations.clear();
477}
478
479CompilerDriver::ConfigData*
480LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) {
481 CompilerDriver::ConfigData* result = 0;
482 if (!Configurations.empty()) {
483 ConfigDataMap::iterator cIt = Configurations.find(filetype);
484 if ( cIt != Configurations.end() ) {
485 // We found one in the case, return it.
486 result = cIt->second;
487 }
488 }
489 if (result == 0) {
490 // The configuration data doesn't exist, we have to go read it.
491 result = ReadConfigData(filetype);
492 // If we got one, cache it
Reid Spencer52c2dc12004-08-29 19:26:56 +0000493 if (result != 0)
Reid Spencer2594c9a2004-08-13 20:21:22 +0000494 Configurations.insert(std::make_pair(filetype,result));
495 }
496 return result; // Might return 0
497}
498
499// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab