X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FSystem%2FWin32%2FPath.inc;h=15c686d2640d5c2643e9d9ef4beadcd5416ab14b;hb=0c33231eff17edbdabdb186e1fae718cf94b58b2;hp=a8862380e9eb03e53b7577aceacb87a77c961fc0;hpb=45e88d68b3a92c7a61904467f81c3ac3322963d2;p=oota-llvm.git diff --git a/lib/System/Win32/Path.inc b/lib/System/Win32/Path.inc index a8862380e9e..15c686d2640 100644 --- a/lib/System/Win32/Path.inc +++ b/lib/System/Win32/Path.inc @@ -25,6 +25,16 @@ // We need to undo a macro defined in Windows.h, otherwise we won't compile: #undef CopyFile +// Windows happily accepts either forward or backward slashes, though any path +// returned by a Win32 API will have backward slashes. As LLVM code basically +// assumes forward slashes are used, backward slashs are converted where they +// can be introduced into a path. +// +// Another invariant is that a path ends with a slash if and only if the path +// is a root directory. Any other use of a trailing slash is stripped. Unlike +// in Unix, Windows has a rather complicated notion of a root path and this +// invariant helps simply the code. + static void FlipBackSlashes(std::string& s) { for (size_t i = 0; i < s.size(); i++) if (s[i] == '\\') @@ -43,9 +53,18 @@ Path::isValid() const { // and followed by something. size_t len = path.size(); size_t pos = path.rfind(':',len); + size_t rootslash = 0; if (pos != std::string::npos) { if (pos != 1 || !isalpha(path[0]) || len < 3) return false; + rootslash = 2; + } + + // Look for a UNC path, and if found adjust our notion of the root slash. + if (len > 3 && path[0] == '/' && path[1] == '/') { + rootslash = path.find('/', 2); + if (rootslash == std::string::npos) + rootslash = 0; } // Check for illegal characters. @@ -55,17 +74,33 @@ Path::isValid() const { != std::string::npos) return false; - // A file or directory name may not end in a period. - if (path[len-1] == '.') - return false; - if (len >= 2 && path[len-2] == '.' && path[len-1] == '/') - return false; + // Remove trailing slash, unless it's a root slash. + if (len > rootslash+1 && path[len-1] == '/') + path.erase(--len); - // A file or directory name may not end in a space. - if (path[len-1] == ' ') - return false; - if (len >= 2 && path[len-2] == ' ' && path[len-1] == '/') - return false; + // Check each component for legality. + for (pos = 0; pos < len; ++pos) { + // A component may not end in a space. + if (path[pos] == ' ') { + if (path[pos+1] == '/' || path[pos+1] == '\0') + return false; + } + + // A component may not end in a period. + if (path[pos] == '.') { + if (path[pos+1] == '/' || path[pos+1] == '\0') { + // Unless it is the pseudo-directory "."... + if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':') + return true; + // or "..". + if (pos > 0 && path[pos-1] == '.') { + if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':') + return true; + } + return false; + } + } + } return true; } @@ -82,19 +117,19 @@ Path::GetTemporaryDirectory() { throw std::string("Can't determine temporary directory"); Path result; - result.setDirectory(pathname); + result.set(pathname); // Append a subdirectory passed on our process id so multiple LLVMs don't // step on each other's toes. - sprintf(pathname, "LLVM_%u", GetCurrentProcessId()); - result.appendDirectory(pathname); + sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId())); + result.appendComponent(pathname); // If there's a directory left over from a previous LLVM execution that // happened to have the same process id, get rid of it. - result.destroyDirectory(true); + result.eraseFromDisk(true); // And finally (re-)create the empty directory. - result.createDirectory(false); + result.createDirectoryOnDisk(false); TempDirectory = new Path(result); return *TempDirectory; } @@ -116,7 +151,7 @@ Path::Path(const std::string& unverified_path) Path Path::GetRootDirectory() { Path result; - result.setDirectory("/"); + result.set("C:/"); return result; } @@ -124,25 +159,25 @@ static void getPathList(const char*path, std::vector& Paths) { const char* at = path; const char* delim = strchr(at, ';'); Path tmpPath; - while( delim != 0 ) { + while (delim != 0) { std::string tmp(at, size_t(delim-at)); - if (tmpPath.setDirectory(tmp)) - if (tmpPath.readable()) + if (tmpPath.set(tmp)) + if (tmpPath.canRead()) Paths.push_back(tmpPath); at = delim + 1; delim = strchr(at, ';'); } + if (*at != 0) - if (tmpPath.setDirectory(std::string(at))) - if (tmpPath.readable()) + if (tmpPath.set(std::string(at))) + if (tmpPath.canRead()) Paths.push_back(tmpPath); - } -void +void Path::GetSystemLibraryPaths(std::vector& Paths) { - Paths.push_back(sys::Path("C:\\WINDOWS\\SYSTEM32\\")); - Paths.push_back(sys::Path("C:\\WINDOWS\\")); + Paths.push_back(sys::Path("C:/WINDOWS/SYSTEM32")); + Paths.push_back(sys::Path("C:/WINDOWS")); } void @@ -154,8 +189,8 @@ Path::GetBytecodeLibraryPaths(std::vector& Paths) { #ifdef LLVM_LIBDIR { Path tmpPath; - if (tmpPath.setDirectory(LLVM_LIBDIR)) - if (tmpPath.readable()) + if (tmpPath.set(LLVM_LIBDIR)) + if (tmpPath.canRead()) Paths.push_back(tmpPath); } #endif @@ -165,7 +200,7 @@ Path::GetBytecodeLibraryPaths(std::vector& Paths) { Path Path::GetLLVMDefaultConfigDir() { // TODO: this isn't going to fly on Windows - return Path("/etc/llvm/"); + return Path("/etc/llvm"); } Path @@ -174,7 +209,7 @@ Path::GetUserHomeDirectory() { const char* home = getenv("HOME"); if (home) { Path result; - if (result.setDirectory(home)) + if (result.set(home)) return result; } return GetRootDirectory(); @@ -183,12 +218,42 @@ Path::GetUserHomeDirectory() { bool Path::isFile() const { - return (isValid() && path[path.length()-1] != '/'); + WIN32_FILE_ATTRIBUTE_DATA fi; + BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi); + if (rc) + return !(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + else if (GetLastError() != ERROR_FILE_NOT_FOUND) { + ThrowError("isFile(): " + std::string(path) + ": Can't get status: "); + } + return false; } bool Path::isDirectory() const { - return (isValid() && path[path.length()-1] == '/'); + WIN32_FILE_ATTRIBUTE_DATA fi; + BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi); + if (rc) + return fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + else if (GetLastError() != ERROR_FILE_NOT_FOUND) + ThrowError("isDirectory(): " + std::string(path) + ": Can't get status: "); + return false; +} + +bool +Path::isHidden() const { + WIN32_FILE_ATTRIBUTE_DATA fi; + BOOL rc = GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi); + if (rc) + return fi.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN; + else if (GetLastError() != ERROR_FILE_NOT_FOUND) + ThrowError("isHidden(): " + std::string(path) + ": Can't get status: "); + return false; +} + +bool +Path::isRootDirectory() const { + size_t len = path.size(); + return len > 0 && path[len-1] == '/'; } std::string @@ -200,7 +265,11 @@ Path::getBasename() const { else slash++; - return path.substr(slash, path.rfind('.')); + size_t dot = path.rfind('.'); + if (dot == std::string::npos || dot < slash) + return path.substr(slash); + else + return path.substr(slash, dot - slash); } bool Path::hasMagicNumber(const std::string &Magic) const { @@ -210,8 +279,10 @@ bool Path::hasMagicNumber(const std::string &Magic) const { return false; } -bool +bool Path::isBytecodeFile() const { + if (!isFile()) + return false; std::string actualMagic; if (!getMagicNumber(actualMagic, 4)) return false; @@ -225,21 +296,21 @@ Path::exists() const { } bool -Path::readable() const { +Path::canRead() const { // FIXME: take security attributes into account. DWORD attr = GetFileAttributes(path.c_str()); return attr != INVALID_FILE_ATTRIBUTES; } bool -Path::writable() const { +Path::canWrite() const { // FIXME: take security attributes into account. DWORD attr = GetFileAttributes(path.c_str()); return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY); } bool -Path::executable() const { +Path::canExecute() const { // FIXME: take security attributes into account. DWORD attr = GetFileAttributes(path.c_str()); return attr != INVALID_FILE_ATTRIBUTES; @@ -254,15 +325,10 @@ Path::getLast() const { if (pos == std::string::npos) return path; - // If the last character is a slash - if (pos == path.length()-1) { - // Find the second to last slash - size_t pos2 = path.rfind('/', pos-1); - if (pos2 == std::string::npos) - return path.substr(0,pos); - else - return path.substr(pos2+1,pos-pos2-1); - } + // If the last character is a slash, we have a root directory + if (pos == path.length()-1) + return path; + // Return everything after the last slash return path.substr(pos+1); } @@ -271,10 +337,10 @@ void Path::getStatusInfo(StatusInfo& info) const { WIN32_FILE_ATTRIBUTE_DATA fi; if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) - ThrowError(std::string(path) + ": Can't get status: "); + ThrowError("getStatusInfo():" + std::string(path) + ": Can't get status: "); info.fileSize = fi.nFileSizeHigh; - info.fileSize <<= 32; + info.fileSize <<= sizeof(fi.nFileSizeHigh)*8; info.fileSize += fi.nFileSizeLow; info.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777; @@ -285,10 +351,6 @@ Path::getStatusInfo(StatusInfo& info) const { info.modTime.fromWin32Time(ft); info.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - if (info.isDir && path[path.length() - 1] != '/') - path += '/'; - else if (!info.isDir && path[path.length() - 1] == '/') - path.erase(path.length() - 1); } static bool AddPermissionBits(const std::string& Filename, int bits) { @@ -307,11 +369,11 @@ static bool AddPermissionBits(const std::string& Filename, int bits) { return true; } -void Path::makeReadable() { +void Path::makeReadableOnDisk() { // All files are readable on Windows (ignoring security attributes). } -void Path::makeWriteable() { +void Path::makeWriteableOnDisk() { DWORD attr = GetFileAttributes(path.c_str()); // If it doesn't exist, we're done. @@ -324,7 +386,7 @@ void Path::makeWriteable() { } } -void Path::makeExecutable() { +void Path::makeExecutableOnDisk() { // All files are executable on Windows (ignoring security attributes). } @@ -335,9 +397,15 @@ Path::getDirectoryContents(std::set& result) const { result.clear(); WIN32_FIND_DATA fd; - HANDLE h = FindFirstFile(path.c_str(), &fd); + std::string searchpath = path; + if (path.size() == 0 || searchpath[path.size()-1] == '/') + searchpath += "*"; + else + searchpath += "/*"; + + HANDLE h = FindFirstFile(searchpath.c_str(), &fd); if (h == INVALID_HANDLE_VALUE) { - if (GetLastError() == ERROR_NO_MORE_FILES) + if (GetLastError() == ERROR_FILE_NOT_FOUND) return true; // not really an error, now is it? ThrowError(path + ": Can't read directory: "); } @@ -345,9 +413,8 @@ Path::getDirectoryContents(std::set& result) const { do { if (fd.cFileName[0] == '.') continue; - Path aPath(path + &fd.cFileName[0]); - if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - aPath.path += "/"; + Path aPath(path); + aPath.appendComponent(&fd.cFileName[0]); result.insert(aPath); } while (FindNextFile(h, &fd)); @@ -361,130 +428,94 @@ Path::getDirectoryContents(std::set& result) const { } bool -Path::setDirectory(const std::string& a_path) { +Path::set(const std::string& a_path) { if (a_path.size() == 0) return false; - Path save(*this); + std::string save(path); path = a_path; FlipBackSlashes(path); - size_t last = a_path.size() -1; - if (a_path[last] != '/') - path += '/'; if (!isValid()) { - path = save.path; + path = save; return false; } return true; } bool -Path::setFile(const std::string& a_path) { - if (a_path.size() == 0) - return false; - Path save(*this); - path = a_path; - FlipBackSlashes(path); - size_t last = a_path.size() - 1; - while (last > 0 && a_path[last] == '/') - last--; - path.erase(last+1); - if (!isValid()) { - path = save.path; +Path::appendComponent(const std::string& name) { + if (name.empty()) return false; + std::string save(path); + if (!path.empty()) { + size_t last = path.size() - 1; + if (path[last] != '/') + path += '/'; } - return true; -} - -bool -Path::appendDirectory(const std::string& dir) { - if (isFile()) - return false; - Path save(*this); - path += dir; - path += "/"; + path += name; if (!isValid()) { - path = save.path; + path = save; return false; } return true; } bool -Path::elideDirectory() { - if (isFile()) - return false; +Path::eraseComponent() { size_t slashpos = path.rfind('/',path.size()); - if (slashpos == 0 || slashpos == std::string::npos) - return false; - if (slashpos == path.size() - 1) - slashpos = path.rfind('/',slashpos-1); - if (slashpos == std::string::npos) + if (slashpos == path.size() - 1 || slashpos == std::string::npos) return false; + std::string save(path); path.erase(slashpos); - return true; -} - -bool -Path::appendFile(const std::string& file) { - if (!isDirectory()) - return false; - Path save(*this); - path += file; if (!isValid()) { - path = save.path; + path = save; return false; } return true; } -bool -Path::elideFile() { - if (isDirectory()) - return false; - size_t slashpos = path.rfind('/',path.size()); - if (slashpos == std::string::npos) - return false; - path.erase(slashpos+1); - return true; -} - bool Path::appendSuffix(const std::string& suffix) { - if (isDirectory()) - return false; - Path save(*this); + std::string save(path); path.append("."); path.append(suffix); if (!isValid()) { - path = save.path; + path = save; return false; } return true; } bool -Path::elideSuffix() { - if (isDirectory()) return false; +Path::eraseSuffix() { size_t dotpos = path.rfind('.',path.size()); size_t slashpos = path.rfind('/',path.size()); - if (slashpos != std::string::npos && dotpos != std::string::npos && - dotpos > slashpos) { - path.erase(dotpos, path.size()-dotpos); - return true; + if (dotpos != std::string::npos) { + if (slashpos == std::string::npos || dotpos > slashpos+1) { + std::string save(path); + path.erase(dotpos, path.size()-dotpos); + if (!isValid()) { + path = save; + return false; + } + return true; + } } return false; } - bool -Path::createDirectory( bool create_parents) { - // Make sure we're dealing with a directory - if (!isDirectory()) return false; - +Path::createDirectoryOnDisk(bool create_parents) { // Get a writeable copy of the path name - char *pathname = reinterpret_cast(_alloca(path.length()+1)); - path.copy(pathname,path.length()); - pathname[path.length()] = 0; + size_t len = path.length(); + char *pathname = reinterpret_cast(_alloca(len+2)); + path.copy(pathname, len); + pathname[len] = 0; + + // Make sure it ends with a slash. + if (len == 0 || pathname[len - 1] != '/') { + pathname[len] = '/'; + pathname[++len] = 0; + } // Determine starting point for initial / search. char *next = pathname; @@ -519,7 +550,7 @@ Path::createDirectory( bool create_parents) { } } else { // Drop trailing slash. - pathname[path.size()-1] = 0; + pathname[len-1] = 0; if (!CreateDirectory(pathname, NULL)) { ThrowError(std::string(pathname) + ": Can't create directory: "); } @@ -528,10 +559,7 @@ Path::createDirectory( bool create_parents) { } bool -Path::createFile() { - // Make sure we're dealing with a file - if (!isFile()) return false; - +Path::createFileOnDisk() { // Create the file HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); @@ -543,86 +571,87 @@ Path::createFile() { } bool -Path::destroyDirectory(bool remove_contents) const { - // Make sure we're dealing with a directory - if (!isDirectory()) return false; +Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const { + if (isFile()) { + DWORD attr = GetFileAttributes(path.c_str()); - // If it doesn't exist, we're done. - if (!exists()) return true; + // If it doesn't exist, we're done. + if (attr == INVALID_FILE_ATTRIBUTES) + return false; - char *pathname = reinterpret_cast(_alloca(path.length()+1)); - path.copy(pathname,path.length()+1); - int lastchar = path.length() - 1 ; - if (pathname[lastchar] == '/') - pathname[lastchar] = 0; + // Read-only files cannot be deleted on Windows. Must remove the read-only + // attribute first. + if (attr & FILE_ATTRIBUTE_READONLY) { + if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) + return GetError(path + ": Can't destroy file: ", ErrStr); + } - if (remove_contents) { - WIN32_FIND_DATA fd; - HANDLE h = FindFirstFile(path.c_str(), &fd); - - // It's a bad idea to alter the contents of a directory while enumerating - // its contents. So build a list of its contents first, then destroy them. - - if (h != INVALID_HANDLE_VALUE) { - std::vector list; - - do { - if (strcmp(fd.cFileName, ".") == 0) - continue; - if (strcmp(fd.cFileName, "..") == 0) - continue; - - Path aPath(path + &fd.cFileName[0]); - if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - aPath.path += "/"; - list.push_back(aPath); - } while (FindNextFile(h, &fd)); - - DWORD err = GetLastError(); - FindClose(h); - if (err != ERROR_NO_MORE_FILES) { - SetLastError(err); - ThrowError(path + ": Can't read directory: "); - } + if (!DeleteFile(path.c_str())) + ThrowError(path + ": Can't destroy file: "); + return true; + } else if (isDirectory()) { + // If it doesn't exist, we're done. + if (!exists()) + return false; - for (std::vector::iterator I = list.begin(); I != list.end(); ++I) { - Path &aPath = *I; - if (aPath.isDirectory()) - aPath.destroyDirectory(true); - else - aPath.destroyFile(); + char *pathname = reinterpret_cast(_alloca(path.length()+3)); + int lastchar = path.length() - 1 ; + path.copy(pathname, lastchar+1); + + // Make path end with '/*'. + if (pathname[lastchar] != '/') + pathname[++lastchar] = '/'; + pathname[lastchar+1] = '*'; + pathname[lastchar+2] = 0; + + if (remove_contents) { + WIN32_FIND_DATA fd; + HANDLE h = FindFirstFile(pathname, &fd); + + // It's a bad idea to alter the contents of a directory while enumerating + // its contents. So build a list of its contents first, then destroy them. + + if (h != INVALID_HANDLE_VALUE) { + std::vector list; + + do { + if (strcmp(fd.cFileName, ".") == 0) + continue; + if (strcmp(fd.cFileName, "..") == 0) + continue; + + Path aPath(path); + aPath.appendComponent(&fd.cFileName[0]); + list.push_back(aPath); + } while (FindNextFile(h, &fd)); + + DWORD err = GetLastError(); + FindClose(h); + if (err != ERROR_NO_MORE_FILES) { + SetLastError(err); + return GetError(path + ": Can't read directory: ", ErrStr); + } + + for (std::vector::iterator I = list.begin(); I != list.end(); + ++I) { + Path &aPath = *I; + aPath.eraseFromDisk(true); + } + } else { + if (GetLastError() != ERROR_FILE_NOT_FOUND) + return GetError(path + ": Can't read directory: ", ErrStr); } - } else { - if (GetLastError() != ERROR_NO_MORE_FILES) - ThrowError(path + ": Can't read directory: "); } - } - if (!RemoveDirectory(pathname)) - ThrowError(std::string(pathname) + ": Can't destroy directory: "); - return true; -} - -bool -Path::destroyFile() const { - if (!isFile()) return false; - - DWORD attr = GetFileAttributes(path.c_str()); - - // If it doesn't exist, we're done. - if (attr == INVALID_FILE_ATTRIBUTES) + pathname[lastchar] = 0; + if (!RemoveDirectory(pathname)) + return GetError(std::string(pathname) + ": Can't destroy directory: ", + ErrStr); + return false; + } else { + // It appears the path doesn't exist. return true; - - // Read-only files cannot be deleted on Windows. Must remove the read-only - // attribute first. - if (attr & FILE_ATTRIBUTE_READONLY) { - if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) - ThrowError(path + ": Can't destroy file: "); } - - if (!DeleteFile(path.c_str())) - ThrowError(path + ": Can't destroy file: "); - return true; } bool Path::getMagicNumber(std::string& Magic, unsigned len) const { @@ -654,16 +683,16 @@ bool Path::getMagicNumber(std::string& Magic, unsigned len) const { } bool -Path::renameFile(const Path& newName) { - if (!isFile()) return false; - if (!MoveFile(path.c_str(), newName.c_str())) - ThrowError("Can't move '" + path + +Path::renamePathOnDisk(const Path& newName) { + if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING)) + ThrowError("Can't move '" + path + "' to '" + newName.path + "': "); return true; } bool -Path::setStatusInfo(const StatusInfo& si) const { +Path::setStatusInfoOnDisk(const StatusInfo& si) const { + // FIXME: should work on directories also. if (!isFile()) return false; HANDLE h = CreateFile(path.c_str(), @@ -713,44 +742,51 @@ Path::setStatusInfo(const StatusInfo& si) const { return true; } -void -sys::CopyFile(const sys::Path &Dest, const sys::Path &Src) { +void +CopyFile(const sys::Path &Dest, const sys::Path &Src) { // Can't use CopyFile macro defined in Windows.h because it would mess up the // above line. We use the expansion it would have in a non-UNICODE build. if (!::CopyFileA(Src.c_str(), Dest.c_str(), false)) - ThrowError("Can't copy '" + Src.toString() + + ThrowError("Can't copy '" + Src.toString() + "' to '" + Dest.toString() + "': "); } -void +void Path::makeUnique(bool reuse_current) { if (reuse_current && !exists()) return; // File doesn't exist already, just use it! - Path dir (*this); - dir.elideFile(); - std::string fname = this->getLast(); - - char newName[MAX_PATH + 1]; - if (!GetTempFileName(dir.c_str(), fname.c_str(), 0, newName)) - ThrowError("Cannot make unique filename for '" + path + "': "); + // Reserve space for -XXXXXX at the end. + char *FNBuffer = (char*) alloca(path.size()+8); + unsigned offset = path.size(); + path.copy(FNBuffer, offset); - path = newName; + // Find a numeric suffix that isn't used by an existing file. Assume there + // won't be more than 1 million files with the same prefix. Probably a safe + // bet. + static unsigned FCounter = 0; + do { + sprintf(FNBuffer+offset, "-%06u", FCounter); + if (++FCounter > 999999) + FCounter = 0; + path = FNBuffer; + } while (exists()); } bool -Path::createTemporaryFile(bool reuse_current) { - // Make sure we're dealing with a file - if (!isFile()) +Path::createTemporaryFileOnDisk(bool reuse_current) { + // Make this into a unique file name + makeUnique(reuse_current); + + // Now go and create it + HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) return false; - // Make this into a unique file name - makeUnique( reuse_current ); + CloseHandle(h); return true; } } } - -// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab -