00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include <sys/types.h>
00027 #include <sys/stat.h>
00028 #include <unistd.h>
00029 #include <dirent.h>
00030 #include <fcntl.h>
00031 #include <stlib/tools/Filename.h>
00032
00033 #include <stlib/ByteArray.h>
00034 #include <stlib/Character.h>
00035 #include <stlib/ExternalReadStream.h>
00036 #include <stlib/ExternalReadAppendStream.h>
00037 #include <stlib/ExternalWriteStream.h>
00038 #include <stlib/FileAccessor.h>
00039 #include <stlib/OrderedCollection.h>
00040 #include <stlib/ReadStream.h>
00041 #include <stlib/Stream.h>
00042 #include <stlib/String.h>
00043 #include <stlib/WriteStream.h>
00044 #include <stlib/tools/FileEncodedStreamFactory.h>
00045
00046 #include <stlib/Error.h>
00047
00048 using namespace Tools;
00049
00050 Filename::Filename(String *name)
00051 {
00052 public_name = name;
00053 local_name = public_name->asCString();
00054 }
00055
00056 Filename::Filename(const char *name)
00057 {
00058 public_name = new String(name);
00059 local_name = public_name->asCString();
00060 }
00061
00062 Filename::~Filename(void)
00063 {
00064 GC_free(local_name);
00065 }
00066
00067
00068 String *Filename::className(void) const
00069 {
00070 return new String("Filename");
00071 }
00072
00073
00074 Filename *Filename::construct(String *extra)
00075 {
00076 return new Filename(constructString(extra));
00077 }
00078
00079 Filename *Filename::construct(const char *extra)
00080 {
00081 return construct(new String(extra));
00082 }
00083
00084 Filename *Filename::construct(Filename *extra)
00085 {
00086 return construct(extra->asString());
00087 }
00088
00089 String *Filename::constructString(String *extra)
00090 {
00091 Character *sep = separator();
00092 String *prefix = asString();
00093 String *suffix;
00094 Stream *ws;
00095 if (!extra->isEmpty() && extra->first()->isEqual(sep)) {
00096 suffix = dynamic_cast<String *>(extra->copy(1, extra->size()));
00097 } else {
00098 suffix = extra;
00099 }
00100 ws = (new String(prefix->size() + suffix->size() + 1))->writeStream();
00101 ws->nextPutAll(prefix);
00102 if (prefix->isEmpty() || !prefix->first()->isEqual(sep)) {
00103 ws->nextPut(sep);
00104 }
00105 ws->nextPutAll(suffix);
00106 return dynamic_cast<String *>(ws->contents());
00107 }
00108
00109
00110 Character *Filename::separator(void)
00111 {
00112 return Character::value('/');
00113 }
00114
00115
00116 Filename *Filename::currentDirectory(void)
00117 {
00118 return new Filename(currentDirectoryString());
00119 }
00120
00121 String *Filename::currentDirectoryString(void)
00122 {
00123 char *buffer = getcwd(NULL, 0);
00124 String *fn = new String(buffer);
00125 free((void *) buffer);
00126 return fn;
00127 }
00128
00129
00130 OrderedCollection *Filename::filesMatching(String *pattern)
00131 {
00132 OrderedCollection *allFiles = new OrderedCollection;
00133 OrderedCollection *list = components(pattern);
00134
00135 if (list->isEmpty()) return allFiles;
00136 baseDirectoryForList(list)->filesMatchingAccessList(list, allFiles);
00137 return allFiles;
00138 }
00139
00140
00141 bool Filename::isCaseSensitive(void)
00142 {
00143 return true;
00144 }
00145
00146
00147 const char *Filename::localName(void) const
00148 {
00149 return local_name;
00150 }
00151
00152
00153 long Filename::hash(void) const
00154 {
00155 return asString()->asUppercase()->hash();
00156 }
00157
00158 bool Filename::isEqual(const Object *obj) const
00159 {
00160 if (!obj->className()->isEqual(className())) return false;
00161 const Filename *fn = dynamic_cast<const Filename *>(obj);
00162 if (isCaseSensitive()) {
00163 return asString()->isEqual(fn->asString());
00164 } else {
00165
00166 return asString()->asUppercase()->isEqual(fn->asString()->asUppercase());
00167 }
00168 }
00169
00170
00171 Filename *Filename::asFilename(void)
00172 {
00173 return this;
00174 }
00175
00176 String *Filename::asString(void) const
00177 {
00178 return public_name;
00179 }
00180
00181
00182 void Filename::beCurrentDirectory(void)
00183 {
00184 if (!(exists() && isDirectory())) {
00185 error(new String("Receiver must be an existing directory."),
00186 new String(__PRETTY_FUNCTION__));
00187 }
00188 setCurrentDirectory(this);
00189 }
00190
00191 void Filename::copyTo(Filename *destName)
00192 {
00193 if (link(localName(), destName->localName()) == 0) return;
00194 privateCopy(FileAccessor::openFileReadOnly(asString()),
00195 FileAccessor::openFileWriteOnly(destName->asString()));
00196 }
00197
00198 void Filename::copyTo(String *destName)
00199 {
00200 copyTo(new Filename(destName));
00201 }
00202
00203 OrderedCollection *Filename::directoryContents(void)
00204 {
00205 OrderedCollection *allFiles = new OrderedCollection;
00206 DIR *dir;
00207 struct dirent *entry;
00208
00209 dir = opendir(localName());
00210 if (dir == NULL) return allFiles;
00211 while ((entry = readdir(dir)) != NULL) {
00212 allFiles->add(new String(entry->d_name));
00213 }
00214 closedir(dir);
00215 return allFiles;
00216 }
00217
00218 void Filename::erase(void)
00219 {
00220 int retCode = remove(localName());
00221 if (retCode < 0) {
00222 error(new String("Unexpected error"),
00223 new String(__PRETTY_FUNCTION__));
00224 }
00225 }
00226
00227 long Filename::fileSize(void)
00228 {
00229 struct stat buf;
00230
00231 stat(localName(), &buf);
00232 return buf.st_size;
00233 }
00234
00235 void Filename::makeDirectory(void)
00236 {
00237 int retCode = mkdir(localName(), 0777);
00238 if (retCode < 0) {
00239 error(new String("Unexpected error"),
00240 new String(__PRETTY_FUNCTION__));
00241 }
00242 }
00243
00244 void Filename::moveTo(Filename *destName)
00245 {
00246 copyTo(destName);
00247 erase();
00248 }
00249
00250 void Filename::moveTo(String *destName)
00251 {
00252 moveTo(new Filename(destName));
00253 }
00254
00255 void Filename::renameTo(Filename *destName)
00256 {
00257 if (rename(localName(), destName->localName()) == 0) return;
00258 moveTo(destName);
00259 }
00260
00261 void Filename::renameTo(String *destName)
00262 {
00263 renameTo(new Filename(destName));
00264 }
00265
00266
00267 Filename *Filename::baseDirectoryForList(OrderedCollection *list)
00268 {
00269 if (dynamic_cast<String *>(list->first())->first()->isEqual(separator())) {
00270 return new Filename(dynamic_cast<String *>(list->removeFirst()));
00271 } else {
00272 return currentDirectory();
00273 }
00274 }
00275
00276 String *Filename::readBaseLocationFromStream(PositionableStream *stream)
00277 {
00278 if (stream->peekFor(separator())) {
00279 if (stream->peekFor(separator())) {
00280 return String::with(separator(), separator());
00281 }
00282 return String::with(separator());
00283 }
00284 return new String;
00285 }
00286
00287 OrderedCollection *Filename::components(void)
00288 {
00289 return components(asString());
00290 }
00291
00292 OrderedCollection *Filename::components(String *name)
00293 {
00294 OrderedCollection *components = new OrderedCollection;
00295 ReadStream *stream = name->readStream();
00296
00297 String *component = readBaseLocationFromStream(stream);
00298 if (!component->isEmpty()) components->add(component);
00299 while (!stream->atEnd())
00300 components->add(stream->upTo(separator()));
00301 return components;
00302 }
00303
00304 Filename *Filename::directory(void)
00305 {
00306 return new Filename(head());
00307 }
00308
00309 String *Filename::extension(void)
00310 {
00311 String *t = tail();
00312 int periodIndex = t->lastIndexOf('.');
00313 if (periodIndex <= 0) return nil;
00314 return dynamic_cast<String *>(t->copy(periodIndex, t->size()));
00315 }
00316
00317 String *Filename::head(void)
00318 {
00319 long index = lastSeparatorIndex();
00320 if (index >= 0) {
00321 index = (index > 1) ? index : 1;
00322 return dynamic_cast<String *>(asString()->copy(0, index));
00323 } else {
00324 return currentDirectoryString();
00325 }
00326 }
00327
00328 String *Filename::tail(void)
00329 {
00330 String *nm = asString();
00331 long index = lastSeparatorIndex();
00332 if (index >= 0) {
00333 return dynamic_cast<String *>(nm->copy(index + 1, nm->size()));
00334 } else {
00335 return dynamic_cast<String *>(nm->copy());
00336 }
00337 }
00338
00339
00340 void Filename::printOn(Stream *stream)
00341 {
00342 Object::printOn(stream);
00343 stream->nextPut('(');
00344 stream->print(public_name);
00345 stream->nextPut(')');
00346 }
00347
00348
00349 FileEncodedStreamFactory *Filename::withEncoding(String *encoding)
00350 {
00351 return new FileEncodedStreamFactory(this, encoding);
00352 }
00353
00354 FileEncodedStreamFactory *Filename::withEncoding(const char *encoding)
00355 {
00356 return withEncoding(new String(encoding));
00357 }
00358
00359 ExternalWriteStream *Filename::appendStream(void)
00360 {
00361 FileAccessor *accessor;
00362 accessor = new FileAccessor(public_name, O_WRONLY, O_APPEND | O_CREAT);
00363 return accessor->writeStream();
00364 }
00365
00366 ExternalReadStream *Filename::readStream(void)
00367 {
00368 return FileAccessor::openFileReadOnly(public_name)->readStream();
00369 }
00370
00371 ExternalReadAppendStream *Filename::readAppendStream(void)
00372 {
00373 FileAccessor *accessor;
00374 accessor = new FileAccessor(public_name, O_RDWR, O_APPEND | O_CREAT);
00375 return accessor->readAppendStream();
00376 }
00377
00378 ExternalWriteStream *Filename::writeStream(void)
00379 {
00380 return FileAccessor::openFileWriteOnly(public_name)->writeStream();
00381 }
00382
00383
00384 bool Filename::exists(void)
00385 {
00386 struct stat buf;
00387
00388 return stat(localName(), &buf) == 0;
00389 }
00390
00391 bool Filename::isAbsolute(void)
00392 {
00393 if (public_name->isEmpty()) return false;
00394 return public_name->first()->isEqual(separator());
00395 }
00396
00397 bool Filename::isDirectory(void)
00398 {
00399 struct stat buf;
00400
00401 stat(localName(), &buf);
00402 return S_ISDIR(buf.st_mode);
00403 }
00404
00405 bool Filename::isReadable(void)
00406 {
00407 struct stat buf;
00408 int mask;
00409
00410 stat(localName(), &buf);
00411 mask = S_IROTH & ((getegid() == buf.st_gid) ? S_IRGRP : 0)
00412 & ((geteuid() == buf.st_uid) ? S_IRUSR : 0);
00413 return (buf.st_mode & mask) != 0;
00414 }
00415
00416 bool Filename::isRelative(void)
00417 {
00418 return !isAbsolute();
00419 }
00420
00421 bool Filename::isWritable(void)
00422 {
00423 struct stat buf;
00424 int mask;
00425
00426 stat(localName(), &buf);
00427 mask = S_IWOTH & ((getegid() == buf.st_gid) ? S_IWGRP : 0)
00428 & ((geteuid() == buf.st_uid) ? S_IWUSR : 0);
00429 return (buf.st_mode & mask) != 0;
00430 }
00431
00432 ByteArray *Filename::contentOfEntireFile(void)
00433 {
00434 Stream *stream;
00435 ByteArray *content;
00436
00437 _ensure(
00438 stream = readStream();
00439 content = dynamic_cast<ByteArray *>(stream->contents())
00440 ,
00441 stream->close()
00442 );
00443
00444 return content;
00445 }
00446
00447
00448 OrderedCollection *Filename::filesMatchingAccessList(OrderedCollection *list,
00449 OrderedCollection *result)
00450 {
00451 if (list->isEmpty()) return result;
00452 if (dynamic_cast<String *>(list->first())->isEqual(".")) {
00453 list->removeFirst();
00454 return filesMatchingAccessList(list, result);
00455 }
00456 if (dynamic_cast<String *>(list->first())->isEqual("..")) {
00457 list->removeFirst();
00458 return (construct("..")->filesMatchingAccessList(list, result));
00459 }
00460 return privFilesMatchingAccessList(list, result);
00461 }
00462
00463 long Filename::lastSeparatorIndex(void)
00464 {
00465 return asString()->lastIndexOf(separator());
00466 }
00467
00468 void Filename::privateCopy(FileAccessor *source, FileAccessor *destination)
00469 {
00470 int bufSize = source->bufferSize();
00471 ByteArray *buffer = new ByteArray(bufSize);
00472 int amountRead;
00473
00474 _ensure (
00475 while ((amountRead = source->readInto(buffer)) > 0) {
00476 destination->writeFrom(buffer, 0, amountRead);
00477 }
00478 ,
00479 source->close();
00480 destination->close();
00481 );
00482 }
00483
00484 OrderedCollection *Filename::privFilesMatchingAccessList(OrderedCollection *list,
00485 OrderedCollection *result)
00486 {
00487 Filename *fullname;
00488 String *pref = dynamic_cast<String *>(list->first());
00489 OrderedCollection *sublist;
00490
00491 sublist = dynamic_cast<OrderedCollection *>(list->copy(1, list->size()));
00492 if (!(pref->includes('*') || pref->includes('#'))) {
00493 fullname = construct(pref);
00494 if (sublist->isEmpty()) {
00495 if (fullname->exists()) result->add(fullname->asString());
00496 } else {
00497 if (fullname->exists() && fullname->isDirectory())
00498 filesMatchingAccessList(sublist, result);
00499 }
00500 return result;
00501 }
00502
00503 OrderedCollection *contents = directoryContents();
00504 int length = contents->size();
00505 for (int i = 0; i < length; i++) {
00506 String *name = dynamic_cast<String *>(contents->at(i));
00507 if (pref->match(name, !isCaseSensitive())) {
00508 fullname = construct(name);
00509 if (sublist->isEmpty()) {
00510 result->add(fullname->asString());
00511 } else {
00512 if (fullname->isDirectory())
00513 filesMatchingAccessList(sublist, result);
00514 }
00515 }
00516 }
00517 return result;
00518 }
00519
00520 void Filename::setCurrentDirectory(Filename *directory)
00521 {
00522 int retCode = chdir(directory->localName());
00523 if (retCode < 0) {
00524 (new Error(new String("Unexpected error"),
00525 new String(__PRETTY_FUNCTION__),
00526 directory))->raise();
00527 }
00528 }