blob: 4770875833c76d6109f78ec9dc7ac2cbac29c2d8 [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 Spencerbae68252004-08-19 04:49:47 +000093 int next() {
94 token = Configlex();
95 if (DumpTokens)
96 std::cerr << token << "\n";
97 return token;
98 }
Reid Spencer68fb37a2004-08-14 09:37:15 +000099
100 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 Spencer68fb37a2004-08-14 09:37:15 +0000105 void eatLineRemnant() {
106 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 Spencerbae68252004-08-19 04:49:47 +0000165 default:
166 return false;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000167 }
Reid Spencerbae68252004-08-19 04:49:47 +0000168 return true;
169 }
170
171 void parseOptionList(CompilerDriver::StringVector& optList ) {
172 if (next() == EQUALS) {
173 while (next_is_real()) {
174 if (token == STRING || token == OPTION)
175 optList.push_back(ConfigLexerState.StringVal);
176 else if (!parseSubstitution(optList)) {
177 error("Expecting a program argument or substitution", false);
178 break;
179 }
180 }
181 } else
182 error("Expecting '='");
Reid Spencerbf437722004-08-15 08:19:46 +0000183 }
184
Reid Spencer59a745a2004-08-22 18:03:25 +0000185 void parseVersion() {
186 if (next() == EQUALS) {
187 while (next_is_real()) {
188 if (token == STRING || token == OPTION)
189 confDat->version = ConfigLexerState.StringVal;
190 else
191 error("Expecting a version string");
192 }
193 } else
194 error("Expecting '='");
195 }
196
Reid Spencerbf437722004-08-15 08:19:46 +0000197 void parseLang() {
198 switch (next() ) {
199 case NAME:
200 confDat->langName = parseName();
201 break;
202 case OPT1:
203 parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]);
204 break;
205 case OPT2:
206 parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]);
207 break;
208 case OPT3:
209 parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]);
210 break;
211 case OPT4:
212 parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]);
213 break;
214 case OPT5:
215 parseOptionList(
216 confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]);
217 break;
218 default:
219 error("Expecting 'name' or 'optN' after 'lang.'");
220 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000221 }
222 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000223
Reid Spencer68fb37a2004-08-14 09:37:15 +0000224 void parseCommand(CompilerDriver::Action& action) {
225 if (next() == EQUALS) {
Reid Spencerbf437722004-08-15 08:19:46 +0000226 if (next() == EOLTOK) {
Reid Spencer68fb37a2004-08-14 09:37:15 +0000227 // no value (valid)
228 action.program.clear();
229 action.args.clear();
Reid Spencer68fb37a2004-08-14 09:37:15 +0000230 } else {
231 if (token == STRING || token == OPTION) {
Reid Spencerbae68252004-08-19 04:49:47 +0000232 action.program = ConfigLexerState.StringVal;
Reid Spencer2594c9a2004-08-13 20:21:22 +0000233 } else {
Reid Spencer68fb37a2004-08-14 09:37:15 +0000234 error("Expecting a program name");
235 }
236 while (next_is_real()) {
Reid Spencerbf437722004-08-15 08:19:46 +0000237 if (token == STRING || token == OPTION) {
Reid Spencerbae68252004-08-19 04:49:47 +0000238 action.args.push_back(ConfigLexerState.StringVal);
239 } else if (!parseSubstitution(action.args)) {
240 error("Expecting a program argument or substitution", false);
Reid Spencerbf437722004-08-15 08:19:46 +0000241 break;
242 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000243 }
244 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000245 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000246 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000247
Reid Spencerbf437722004-08-15 08:19:46 +0000248 void parsePreprocessor() {
249 switch (next()) {
250 case COMMAND:
251 parseCommand(confDat->PreProcessor);
252 break;
253 case REQUIRED:
254 if (parseBoolean())
255 confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG);
256 else
257 confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
258 break;
259 default:
Reid Spencerf384db32004-08-24 14:03:23 +0000260 error("Expecting 'command' or 'required' but found '" +
261 ConfigLexerState.StringVal);
Reid Spencerbf437722004-08-15 08:19:46 +0000262 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000263 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000264 }
265
Reid Spencerf384db32004-08-24 14:03:23 +0000266 bool parseOutputFlag() {
267 if (next() == EQUALS) {
268 if (next() == ASSEMBLY) {
269 return true;
270 } else if (token == BYTECODE) {
271 return false;
272 } else {
273 error("Expecting output type value");
274 return false;
275 }
276 if (next() != EOLTOK && token != 0) {
277 error("Extraneous tokens after output value");
278 }
279 }
280 else
281 error("Expecting '='");
282 return false;
283 }
284
Reid Spencer68fb37a2004-08-14 09:37:15 +0000285 void parseTranslator() {
Reid Spencerbf437722004-08-15 08:19:46 +0000286 switch (next()) {
287 case COMMAND:
288 parseCommand(confDat->Translator);
289 break;
290 case REQUIRED:
291 if (parseBoolean())
292 confDat->Translator.set(CompilerDriver::REQUIRED_FLAG);
293 else
294 confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG);
295 break;
296 case PREPROCESSES:
297 if (parseBoolean())
298 confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
299 else
300 confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
301 break;
Reid Spencerf384db32004-08-24 14:03:23 +0000302 case OUTPUT:
303 if (parseOutputFlag())
Reid Spencerbae68252004-08-19 04:49:47 +0000304 confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000305 else
Reid Spencerbae68252004-08-19 04:49:47 +0000306 confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000307 break;
Reid Spencerbae68252004-08-19 04:49:47 +0000308
Reid Spencerbf437722004-08-15 08:19:46 +0000309 default:
Reid Spencerf384db32004-08-24 14:03:23 +0000310 error("Expecting 'command', 'required', 'preprocesses', or "
311 "'output' but found '" + ConfigLexerState.StringVal +
312 "' instead");
Reid Spencerbf437722004-08-15 08:19:46 +0000313 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000314 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000315 }
316
317 void parseOptimizer() {
Reid Spencerbf437722004-08-15 08:19:46 +0000318 switch (next()) {
319 case COMMAND:
320 parseCommand(confDat->Optimizer);
321 break;
Reid Spencerbae68252004-08-19 04:49:47 +0000322 case PREPROCESSES:
323 if (parseBoolean())
324 confDat->Optimizer.set(CompilerDriver::PREPROCESSES_FLAG);
325 else
326 confDat->Optimizer.clear(CompilerDriver::PREPROCESSES_FLAG);
327 break;
328 case TRANSLATES:
329 if (parseBoolean())
330 confDat->Optimizer.set(CompilerDriver::TRANSLATES_FLAG);
331 else
332 confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG);
333 break;
Reid Spencerf384db32004-08-24 14:03:23 +0000334 case REQUIRED:
Reid Spencerbf437722004-08-15 08:19:46 +0000335 if (parseBoolean())
Reid Spencerf384db32004-08-24 14:03:23 +0000336 confDat->Optimizer.set(CompilerDriver::REQUIRED_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000337 else
Reid Spencerf384db32004-08-24 14:03:23 +0000338 confDat->Optimizer.clear(CompilerDriver::REQUIRED_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000339 break;
Reid Spencerf384db32004-08-24 14:03:23 +0000340 case OUTPUT:
341 if (parseOutputFlag())
Reid Spencerbae68252004-08-19 04:49:47 +0000342 confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000343 else
Reid Spencerbae68252004-08-19 04:49:47 +0000344 confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000345 break;
346 default:
Reid Spencerf384db32004-08-24 14:03:23 +0000347 error(std::string("Expecting 'command', 'preprocesses', ") +
348 "'translates' or 'output' but found '" +
349 ConfigLexerState.StringVal + "' instead");
Reid Spencerbf437722004-08-15 08:19:46 +0000350 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000351 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000352 }
353
354 void parseAssembler() {
Reid Spencerbf437722004-08-15 08:19:46 +0000355 switch(next()) {
356 case COMMAND:
357 parseCommand(confDat->Assembler);
358 break;
359 default:
360 error("Expecting 'command'");
361 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000362 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000363 }
364
365 void parseLinker() {
Reid Spencerbf437722004-08-15 08:19:46 +0000366 switch(next()) {
Reid Spencerf384db32004-08-24 14:03:23 +0000367 case LIBS:
368 break; //FIXME
369 case LIBPATHS:
370 break; //FIXME
Reid Spencerbf437722004-08-15 08:19:46 +0000371 default:
Reid Spencerf384db32004-08-24 14:03:23 +0000372 error("Expecting 'libs' or 'libpaths'");
Reid Spencerbf437722004-08-15 08:19:46 +0000373 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000374 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000375 }
376
377 void parseAssignment() {
378 switch (token) {
Reid Spencer59a745a2004-08-22 18:03:25 +0000379 case VERSION: parseVersion(); break;
Reid Spencerbf437722004-08-15 08:19:46 +0000380 case LANG: parseLang(); break;
381 case PREPROCESSOR: parsePreprocessor(); break;
382 case TRANSLATOR: parseTranslator(); break;
383 case OPTIMIZER: parseOptimizer(); break;
384 case ASSEMBLER: parseAssembler(); break;
385 case LINKER: parseLinker(); break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000386 case EOLTOK: break; // just ignore
387 case ERRORTOK:
388 default:
Reid Spencerbf437722004-08-15 08:19:46 +0000389 error("Invalid top level configuration item");
390 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000391 }
392 }
393
394 void parseFile() {
Reid Spencerbf437722004-08-15 08:19:46 +0000395 while ( next() != EOFTOK ) {
396 if (token == ERRORTOK)
397 error("Invalid token");
398 else if (token != EOLTOK)
399 parseAssignment();
Reid Spencer68fb37a2004-08-14 09:37:15 +0000400 }
401 provider->checkErrors();
402 }
403 };
404
405 void
406 ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
Reid Spencerbf437722004-08-15 08:19:46 +0000407 Parser p;
408 p.token = EOFTOK;
409 p.provider = &provider;
410 p.confDat = &confDat;
Reid Spencerbf437722004-08-15 08:19:46 +0000411 p.parseFile();
Reid Spencer2594c9a2004-08-13 20:21:22 +0000412 }
413}
414
415CompilerDriver::ConfigData*
Reid Spencer68fb37a2004-08-14 09:37:15 +0000416LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
417 CompilerDriver::ConfigData* result = 0;
Reid Spencerb38e4052004-08-20 09:24:07 +0000418 std::string dir_name;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000419 if (configDir.empty()) {
Reid Spencerb38e4052004-08-20 09:24:07 +0000420 // Try the environment variable
421 const char* conf = getenv("LLVM_CONFIG_DIR");
422 if (conf) {
423 dir_name = conf;
424 dir_name += "/";
Reid Spencerf384db32004-08-24 14:03:23 +0000425 if (!::sys::FileIsReadable(dir_name + ftype))
Reid Spencerb38e4052004-08-20 09:24:07 +0000426 throw "Configuration file for '" + ftype + "' is not available.";
427 } else {
428 // Try the user's home directory
429 const char* home = getenv("HOME");
430 if (home) {
431 dir_name = home;
432 dir_name += "/.llvm/etc/";
Reid Spencerf384db32004-08-24 14:03:23 +0000433 if (!::sys::FileIsReadable(dir_name + ftype)) {
Reid Spencerb38e4052004-08-20 09:24:07 +0000434 // Okay, try the LLVM installation directory
435 dir_name = LLVM_ETCDIR;
436 dir_name += "/";
Reid Spencerf384db32004-08-24 14:03:23 +0000437 if (!::sys::FileIsReadable(dir_name + ftype)) {
Reid Spencerb38e4052004-08-20 09:24:07 +0000438 // Okay, try the "standard" place
439 dir_name = "/etc/llvm/";
Reid Spencerf384db32004-08-24 14:03:23 +0000440 if (!::sys::FileIsReadable(dir_name + ftype)) {
Reid Spencerb38e4052004-08-20 09:24:07 +0000441 throw "Configuration file for '" + ftype + "' is not available.";
442 }
443 }
444 }
445 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000446 }
447 } else {
Reid Spencerb38e4052004-08-20 09:24:07 +0000448 dir_name = configDir + "/";
Reid Spencerf384db32004-08-24 14:03:23 +0000449 if (!::sys::FileIsReadable(dir_name + ftype)) {
Reid Spencerb38e4052004-08-20 09:24:07 +0000450 throw "Configuration file for '" + ftype + "' is not available.";
Reid Spencer68fb37a2004-08-14 09:37:15 +0000451 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000452 }
Reid Spencerb38e4052004-08-20 09:24:07 +0000453 FileInputProvider fip( dir_name + ftype );
454 if (!fip.okay()) {
455 throw "Configuration file for '" + ftype + "' is not available.";
456 }
457 result = new CompilerDriver::ConfigData();
458 ParseConfigData(fip,*result);
Reid Spencer68fb37a2004-08-14 09:37:15 +0000459 return result;
Reid Spencer2594c9a2004-08-13 20:21:22 +0000460}
461
462LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider()
463 : Configurations()
464 , configDir()
465{
466 Configurations.clear();
467}
468
469LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
470{
471 ConfigDataMap::iterator cIt = Configurations.begin();
472 while (cIt != Configurations.end()) {
473 CompilerDriver::ConfigData* cd = cIt->second;
474 ++cIt;
475 delete cd;
476 }
477 Configurations.clear();
478}
479
480CompilerDriver::ConfigData*
481LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) {
482 CompilerDriver::ConfigData* result = 0;
483 if (!Configurations.empty()) {
484 ConfigDataMap::iterator cIt = Configurations.find(filetype);
485 if ( cIt != Configurations.end() ) {
486 // We found one in the case, return it.
487 result = cIt->second;
488 }
489 }
490 if (result == 0) {
491 // The configuration data doesn't exist, we have to go read it.
492 result = ReadConfigData(filetype);
493 // If we got one, cache it
494 if ( result != 0 )
495 Configurations.insert(std::make_pair(filetype,result));
496 }
497 return result; // Might return 0
498}
499
500// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab