blob: 5d2a4b688c42fe01c59eb0f3309eb58cfffecadb [file] [log] [blame]
Reid Spencerb89a2232004-08-25 06:20:07 +00001//===- llvm/System/Unix/Path.cpp - Unix Path Implementation -----*- C++ -*-===//
2//
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 Unix specific portion of the Path class.
11//
12//===----------------------------------------------------------------------===//
13
14//===----------------------------------------------------------------------===//
15//=== WARNING: Implementation here must contain only generic UNIX code that
Reid Spencer8e665952004-08-29 05:24:01 +000016//=== is guaranteed to work on *all* UNIX variants.
Reid Spencerb89a2232004-08-25 06:20:07 +000017//===----------------------------------------------------------------------===//
18
Reid Spencer551ccae2004-09-01 22:55:40 +000019#include <llvm/Config/config.h>
Reid Spencerbe31d2a2004-11-16 17:14:08 +000020#include <llvm/Config/alloca.h>
Reid Spencerb89a2232004-08-25 06:20:07 +000021#include "Unix.h"
22#include <sys/stat.h>
23#include <fcntl.h>
Reid Spencer1b554b42004-09-11 04:55:08 +000024#include <fstream>
Reid Spencereaf18152004-11-14 22:08:36 +000025#include <utime.h>
26#include <dirent.h>
Reid Spencerb89a2232004-08-25 06:20:07 +000027
Reid Spencer8e665952004-08-29 05:24:01 +000028namespace llvm {
29using namespace sys;
Reid Spencerb89a2232004-08-25 06:20:07 +000030
Reid Spencer8e665952004-08-29 05:24:01 +000031Path::Path(std::string unverified_path)
32 : path(unverified_path)
Reid Spencerb89a2232004-08-25 06:20:07 +000033{
Reid Spencer8e665952004-08-29 05:24:01 +000034 if (unverified_path.empty())
35 return;
Reid Spencer07adb282004-11-05 22:15:36 +000036 if (this->isValid())
Reid Spencer8e665952004-08-29 05:24:01 +000037 return;
38 // oops, not valid.
39 path.clear();
40 ThrowErrno(unverified_path + ": path is not valid");
Reid Spencerb89a2232004-08-25 06:20:07 +000041}
42
Reid Spencer8e665952004-08-29 05:24:01 +000043Path
44Path::GetRootDirectory() {
45 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +000046 result.setDirectory("/");
Reid Spencer8e665952004-08-29 05:24:01 +000047 return result;
48}
49
Reid Spencer74e72612004-09-14 00:16:39 +000050static inline bool IsLibrary(Path& path, const std::string& basename) {
Reid Spencer07adb282004-11-05 22:15:36 +000051 if (path.appendFile(std::string("lib") + basename)) {
52 if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000053 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000054 else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000055 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000056 else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000057 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000058 else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000059 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000060 } else if (path.elideFile() && path.appendFile(basename)) {
61 if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000062 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000063 else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000064 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000065 else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000066 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000067 else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000068 return true;
Reid Spencer74e72612004-09-14 00:16:39 +000069 }
70 path.clear();
71 return false;
72}
73
74Path
75Path::GetLibraryPath(const std::string& basename,
76 const std::vector<std::string>& LibPaths) {
77 Path result;
78
79 // Try the paths provided
80 for (std::vector<std::string>::const_iterator I = LibPaths.begin(),
81 E = LibPaths.end(); I != E; ++I ) {
Reid Spencer07adb282004-11-05 22:15:36 +000082 if (result.setDirectory(*I) && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000083 return result;
84 }
85
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000086 // Try the LLVM lib directory in the LLVM install area
Reid Spencer07adb282004-11-05 22:15:36 +000087 if (result.setDirectory(LLVM_LIBDIR) && IsLibrary(result,basename))
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000088 return result;
89
Reid Spencer74e72612004-09-14 00:16:39 +000090 // Try /usr/lib
Reid Spencer07adb282004-11-05 22:15:36 +000091 if (result.setDirectory("/usr/lib/") && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000092 return result;
93
94 // Try /lib
Reid Spencer07adb282004-11-05 22:15:36 +000095 if (result.setDirectory("/lib/") && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000096 return result;
97
98 // Can't find it, give up and return invalid path.
99 result.clear();
100 return result;
101}
102
Reid Spencer8e665952004-08-29 05:24:01 +0000103Path
104Path::GetSystemLibraryPath1() {
105 return Path("/lib/");
106}
107
108Path
109Path::GetSystemLibraryPath2() {
110 return Path("/usr/lib/");
111}
112
113Path
114Path::GetLLVMDefaultConfigDir() {
115 return Path("/etc/llvm/");
116}
117
118Path
119Path::GetLLVMConfigDir() {
120 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +0000121 if (result.setDirectory(LLVM_ETCDIR))
Reid Spencer8e665952004-08-29 05:24:01 +0000122 return result;
123 return GetLLVMDefaultConfigDir();
124}
125
126Path
127Path::GetUserHomeDirectory() {
128 const char* home = getenv("HOME");
129 if (home) {
130 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +0000131 if (result.setDirectory(home))
Reid Spencer8e665952004-08-29 05:24:01 +0000132 return result;
133 }
134 return GetRootDirectory();
135}
136
137bool
Reid Spencer07adb282004-11-05 22:15:36 +0000138Path::isFile() const {
139 return (isValid() && path[path.length()-1] != '/');
Reid Spencer1b554b42004-09-11 04:55:08 +0000140}
141
142bool
Reid Spencer07adb282004-11-05 22:15:36 +0000143Path::isDirectory() const {
144 return (isValid() && path[path.length()-1] == '/');
Reid Spencer1b554b42004-09-11 04:55:08 +0000145}
146
147std::string
Reid Spencer07adb282004-11-05 22:15:36 +0000148Path::getBasename() const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000149 // Find the last slash
150 size_t slash = path.rfind('/');
151 if (slash == std::string::npos)
152 slash = 0;
153 else
154 slash++;
155
156 return path.substr(slash, path.rfind('.'));
157}
158
Reid Spencer07adb282004-11-05 22:15:36 +0000159bool Path::hasMagicNumber(const std::string &Magic) const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000160 size_t len = Magic.size();
Reid Spencerbe31d2a2004-11-16 17:14:08 +0000161 assert(len < 1024 && "Request for magic string too long");
162 char* buf = (char*) alloca(1 + len);
163 int fd = ::open(path.c_str(),O_RDONLY);
164 if (fd < 0)
165 return false;
166 if (0 != ::read(fd, buf, len))
167 return false;
168 close(fd);
Reid Spencer1b554b42004-09-11 04:55:08 +0000169 buf[len] = '\0';
170 return Magic == buf;
171}
172
Reid Spencereaf18152004-11-14 22:08:36 +0000173bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
174 if (!isFile())
175 return false;
Reid Spencerbe31d2a2004-11-16 17:14:08 +0000176 assert(len < 1024 && "Request for magic string too long");
177 char* buf = (char*) alloca(1 + len);
178 int fd = ::open(path.c_str(),O_RDONLY);
179 if (fd < 0)
180 return false;
Reid Spencer86ac2dc2004-12-02 09:09:48 +0000181 ssize_t bytes_read = ::read(fd, buf, len);
182 ::close(fd);
183 if (ssize_t(len) != bytes_read) {
184 Magic.clear();
Reid Spencerbe31d2a2004-11-16 17:14:08 +0000185 return false;
Reid Spencer86ac2dc2004-12-02 09:09:48 +0000186 }
187 Magic.assign(buf,len);
Reid Spencereaf18152004-11-14 22:08:36 +0000188 return true;
189}
190
Reid Spencer1b554b42004-09-11 04:55:08 +0000191bool
Reid Spencer07adb282004-11-05 22:15:36 +0000192Path::isBytecodeFile() const {
Reid Spencer9195f372004-11-09 20:26:31 +0000193 char buffer[ 4];
194 buffer[0] = 0;
195 std::ifstream f(path.c_str());
196 f.read(buffer, 4);
197 if (f.bad())
198 ThrowErrno("can't read file signature");
Reid Spencereaf18152004-11-14 22:08:36 +0000199
200 return (buffer[0] == 'l' && buffer[1] == 'l' && buffer[2] == 'v' &&
201 (buffer[3] == 'c' || buffer[3] == 'm'));
Reid Spencer1b554b42004-09-11 04:55:08 +0000202}
203
204bool
Reid Spencer07adb282004-11-05 22:15:36 +0000205Path::isArchive() const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000206 if (readable()) {
Reid Spencer07adb282004-11-05 22:15:36 +0000207 return hasMagicNumber("!<arch>\012");
Reid Spencer1b554b42004-09-11 04:55:08 +0000208 }
209 return false;
210}
211
212bool
Reid Spencer8e665952004-08-29 05:24:01 +0000213Path::exists() const {
214 return 0 == access(path.c_str(), F_OK );
215}
216
217bool
218Path::readable() const {
219 return 0 == access(path.c_str(), F_OK | R_OK );
220}
221
222bool
223Path::writable() const {
224 return 0 == access(path.c_str(), F_OK | W_OK );
225}
226
227bool
228Path::executable() const {
229 return 0 == access(path.c_str(), R_OK | X_OK );
230}
231
232std::string
233Path::getLast() const {
234 // Find the last slash
235 size_t pos = path.rfind('/');
236
237 // Handle the corner cases
238 if (pos == std::string::npos)
239 return path;
240
241 // If the last character is a slash
242 if (pos == path.length()-1) {
243 // Find the second to last slash
244 size_t pos2 = path.rfind('/', pos-1);
245 if (pos2 == std::string::npos)
246 return path.substr(0,pos);
247 else
248 return path.substr(pos2+1,pos-pos2-1);
249 }
250 // Return everything after the last slash
251 return path.substr(pos+1);
252}
253
Reid Spencerb608a812004-11-16 06:15:19 +0000254void
255Path::getStatusInfo(StatusInfo& info) const {
Reid Spencer9195f372004-11-09 20:26:31 +0000256 struct stat buf;
257 if (0 != stat(path.c_str(), &buf)) {
258 ThrowErrno(std::string("Can't get status: ")+path);
259 }
260 info.fileSize = buf.st_size;
Reid Spencereaf18152004-11-14 22:08:36 +0000261 info.modTime.fromEpochTime(buf.st_mtime);
Reid Spencer9195f372004-11-09 20:26:31 +0000262 info.mode = buf.st_mode;
263 info.user = buf.st_uid;
264 info.group = buf.st_gid;
Reid Spencereaf18152004-11-14 22:08:36 +0000265 info.isDir = S_ISDIR(buf.st_mode);
266 if (info.isDir && path[path.length()-1] != '/')
267 path += '/';
268}
269
270bool
Reid Spencerb608a812004-11-16 06:15:19 +0000271Path::getDirectoryContents(std::set<Path>& result) const {
Reid Spencereaf18152004-11-14 22:08:36 +0000272 if (!isDirectory())
273 return false;
274 DIR* direntries = ::opendir(path.c_str());
275 if (direntries == 0)
276 ThrowErrno(path + ": can't open directory");
277
278 result.clear();
279 struct dirent* de = ::readdir(direntries);
280 while (de != 0) {
281 if (de->d_name[0] != '.') {
282 Path aPath(path + (const char*)de->d_name);
283 struct stat buf;
284 if (0 != stat(aPath.path.c_str(), &buf))
285 ThrowErrno(aPath.path + ": can't get status");
286 if (S_ISDIR(buf.st_mode))
287 aPath.path += "/";
Reid Spencerb608a812004-11-16 06:15:19 +0000288 result.insert(aPath);
Reid Spencereaf18152004-11-14 22:08:36 +0000289 }
290 de = ::readdir(direntries);
291 }
292
293 closedir(direntries);
294 return true;
Reid Spencer9195f372004-11-09 20:26:31 +0000295}
296
Reid Spencer8e665952004-08-29 05:24:01 +0000297bool
Reid Spencer07adb282004-11-05 22:15:36 +0000298Path::setDirectory(const std::string& a_path) {
Reid Spencer8e665952004-08-29 05:24:01 +0000299 if (a_path.size() == 0)
300 return false;
301 Path save(*this);
302 path = a_path;
303 size_t last = a_path.size() -1;
304 if (last != 0 && a_path[last] != '/')
305 path += '/';
Reid Spencer07adb282004-11-05 22:15:36 +0000306 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000307 path = save.path;
308 return false;
309 }
310 return true;
311}
312
313bool
Reid Spencer07adb282004-11-05 22:15:36 +0000314Path::setFile(const std::string& a_path) {
Reid Spencer8e665952004-08-29 05:24:01 +0000315 if (a_path.size() == 0)
316 return false;
317 Path save(*this);
318 path = a_path;
319 size_t last = a_path.size() - 1;
320 while (last > 0 && a_path[last] == '/')
321 last--;
322 path.erase(last+1);
Reid Spencer07adb282004-11-05 22:15:36 +0000323 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000324 path = save.path;
325 return false;
326 }
327 return true;
328}
329
330bool
Reid Spencer07adb282004-11-05 22:15:36 +0000331Path::appendDirectory(const std::string& dir) {
332 if (isFile())
Reid Spencer8e665952004-08-29 05:24:01 +0000333 return false;
334 Path save(*this);
335 path += dir;
336 path += "/";
Reid Spencer07adb282004-11-05 22:15:36 +0000337 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000338 path = save.path;
339 return false;
340 }
341 return true;
342}
343
344bool
Reid Spencer07adb282004-11-05 22:15:36 +0000345Path::elideDirectory() {
346 if (isFile())
Reid Spencer8e665952004-08-29 05:24:01 +0000347 return false;
348 size_t slashpos = path.rfind('/',path.size());
349 if (slashpos == 0 || slashpos == std::string::npos)
350 return false;
351 if (slashpos == path.size() - 1)
352 slashpos = path.rfind('/',slashpos-1);
353 if (slashpos == std::string::npos)
354 return false;
355 path.erase(slashpos);
356 return true;
357}
358
359bool
Reid Spencer07adb282004-11-05 22:15:36 +0000360Path::appendFile(const std::string& file) {
361 if (!isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000362 return false;
363 Path save(*this);
364 path += file;
Reid Spencer07adb282004-11-05 22:15:36 +0000365 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000366 path = save.path;
367 return false;
368 }
369 return true;
370}
371
372bool
Reid Spencer07adb282004-11-05 22:15:36 +0000373Path::elideFile() {
374 if (isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000375 return false;
376 size_t slashpos = path.rfind('/',path.size());
377 if (slashpos == std::string::npos)
378 return false;
379 path.erase(slashpos+1);
380 return true;
381}
382
383bool
Reid Spencer07adb282004-11-05 22:15:36 +0000384Path::appendSuffix(const std::string& suffix) {
385 if (isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000386 return false;
387 Path save(*this);
388 path.append(".");
389 path.append(suffix);
Reid Spencer07adb282004-11-05 22:15:36 +0000390 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000391 path = save.path;
392 return false;
393 }
394 return true;
395}
396
397bool
Reid Spencer07adb282004-11-05 22:15:36 +0000398Path::elideSuffix() {
399 if (isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000400 size_t dotpos = path.rfind('.',path.size());
401 size_t slashpos = path.rfind('/',path.size());
402 if (slashpos != std::string::npos && dotpos != std::string::npos &&
403 dotpos > slashpos) {
404 path.erase(dotpos, path.size()-dotpos);
405 return true;
406 }
407 return false;
408}
409
410
411bool
Reid Spencer07adb282004-11-05 22:15:36 +0000412Path::createDirectory( bool create_parents) {
Reid Spencer8e665952004-08-29 05:24:01 +0000413 // Make sure we're dealing with a directory
Reid Spencer07adb282004-11-05 22:15:36 +0000414 if (!isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000415
416 // Get a writeable copy of the path name
417 char pathname[MAXPATHLEN];
418 path.copy(pathname,MAXPATHLEN);
419
420 // Null-terminate the last component
421 int lastchar = path.length() - 1 ;
422 if (pathname[lastchar] == '/')
423 pathname[lastchar] = 0;
Reid Spencereaf18152004-11-14 22:08:36 +0000424 else
425 pathname[lastchar+1] = 0;
Reid Spencer8e665952004-08-29 05:24:01 +0000426
427 // If we're supposed to create intermediate directories
428 if ( create_parents ) {
429 // Find the end of the initial name component
430 char * next = strchr(pathname,'/');
431 if ( pathname[0] == '/')
432 next = strchr(&pathname[1],'/');
433
434 // Loop through the directory components until we're done
435 while ( next != 0 ) {
436 *next = 0;
437 if (0 != access(pathname, F_OK | R_OK | W_OK))
438 if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
439 ThrowErrno(std::string(pathname) + ": Can't create directory");
440 char* save = next;
Reid Spencereaf18152004-11-14 22:08:36 +0000441 next = strchr(next+1,'/');
Reid Spencer8e665952004-08-29 05:24:01 +0000442 *save = '/';
443 }
Reid Spencer8e665952004-08-29 05:24:01 +0000444 }
Reid Spencereaf18152004-11-14 22:08:36 +0000445
446 if (0 != access(pathname, F_OK | R_OK))
447 if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
448 ThrowErrno(std::string(pathname) + ": Can't create directory");
Reid Spencer8e665952004-08-29 05:24:01 +0000449 return true;
450}
451
452bool
Reid Spencer07adb282004-11-05 22:15:36 +0000453Path::createFile() {
Reid Spencer8e665952004-08-29 05:24:01 +0000454 // Make sure we're dealing with a file
Reid Spencer07adb282004-11-05 22:15:36 +0000455 if (!isFile()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000456
457 // Create the file
Reid Spencer622e2202004-09-18 19:25:11 +0000458 int fd = ::creat(path.c_str(), S_IRUSR | S_IWUSR);
459 if (fd < 0)
Reid Spencer9195f372004-11-09 20:26:31 +0000460 ThrowErrno(path + ": Can't create file");
Reid Spencer622e2202004-09-18 19:25:11 +0000461 ::close(fd);
Reid Spencer8e665952004-08-29 05:24:01 +0000462
463 return true;
Reid Spencerb89a2232004-08-25 06:20:07 +0000464}
465
Reid Spencer8e665952004-08-29 05:24:01 +0000466bool
Reid Spencer9195f372004-11-09 20:26:31 +0000467Path::createTemporaryFile() {
468 // Make sure we're dealing with a file
469 if (!isFile()) return false;
470
471 // Append the filename filler
472 char pathname[MAXPATHLEN];
473 path.copy(pathname,MAXPATHLEN);
Reid Spencereaf18152004-11-14 22:08:36 +0000474 pathname[path.length()] = 0;
Reid Spencer9195f372004-11-09 20:26:31 +0000475 strcat(pathname,"XXXXXX");
476 int fd = ::mkstemp(pathname);
477 if (fd < 0) {
478 ThrowErrno(path + ": Can't create temporary file");
479 }
480 path = pathname;
481 ::close(fd);
482 return true;
483}
484
485bool
Reid Spencer07adb282004-11-05 22:15:36 +0000486Path::destroyDirectory(bool remove_contents) {
Reid Spencer8e665952004-08-29 05:24:01 +0000487 // Make sure we're dealing with a directory
Reid Spencer07adb282004-11-05 22:15:36 +0000488 if (!isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000489
490 // If it doesn't exist, we're done.
491 if (!exists()) return true;
492
493 if (remove_contents) {
494 // Recursively descend the directory to remove its content
495 std::string cmd("/bin/rm -rf ");
496 cmd += path;
497 system(cmd.c_str());
498 } else {
499 // Otherwise, try to just remove the one directory
500 char pathname[MAXPATHLEN];
501 path.copy(pathname,MAXPATHLEN);
502 int lastchar = path.length() - 1 ;
503 if (pathname[lastchar] == '/')
504 pathname[lastchar] = 0;
Reid Spencereaf18152004-11-14 22:08:36 +0000505 else
506 pathname[lastchar+1] = 0;
Reid Spencer8e665952004-08-29 05:24:01 +0000507 if ( 0 != rmdir(pathname))
508 ThrowErrno(std::string(pathname) + ": Can't destroy directory");
509 }
510 return true;
511}
512
513bool
Reid Spencer07adb282004-11-05 22:15:36 +0000514Path::destroyFile() {
515 if (!isFile()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000516 if (0 != unlink(path.c_str()))
Reid Spencereaf18152004-11-14 22:08:36 +0000517 ThrowErrno(path + ": Can't destroy file");
518 return true;
519}
520
521bool
522Path::renameFile(const Path& newName) {
523 if (!isFile()) return false;
524 if (0 != rename(path.c_str(), newName.c_str()))
525 ThrowErrno(std::string("can't rename ") + path + " as " + newName.get());
526 return true;
527}
528
529bool
530Path::setStatusInfo(const StatusInfo& si) const {
531 if (!isFile()) return false;
532 struct utimbuf utb;
533 utb.actime = si.modTime.toPosixTime();
534 utb.modtime = utb.actime;
535 if (0 != ::utime(path.c_str(),&utb))
536 ThrowErrno(path + ": can't set file modification time");
537 if (0 != ::chmod(path.c_str(),si.mode))
538 ThrowErrno(path + ": can't set mode");
Reid Spencer8e665952004-08-29 05:24:01 +0000539 return true;
540}
541
Reid Spencerb89a2232004-08-25 06:20:07 +0000542}
543
544// vim: sw=2