Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

Filename.cc

Go to the documentation of this file.
00001 /*
00002  * Filename.cc
00003  *
00004  * Smalltalk like class library for C++
00005  * File name handler and tools.
00006  *
00007  * Copyright (c) 2004 Milan Cermak
00008  */
00009 /*
00010  * This library is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Lesser General Public
00012  * License as published by the Free Software Foundation; either
00013  * version 2.1 of the License, or (at your option) any later version.
00014  *
00015  * This library is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * Lesser General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU Lesser General Public
00021  * License along with this library; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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 /* Class-accessing protocol */
00068 String *Filename::className(void) const
00069 {
00070     return new String("Filename");
00071 }
00072 
00073 /* Instance creation protocol */
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 /* Class-constants protocol */
00110 Character *Filename::separator(void)
00111 {
00112     return Character::value('/');
00113 }
00114 
00115 /* Class-defaults protocol */
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 /* Class-utilities protocol */
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 /* Class-testing protocol */
00141 bool Filename::isCaseSensitive(void)
00142 {
00143     return true;
00144 }
00145 
00146 /* Accessing protocol */
00147 const char *Filename::localName(void) const
00148 {
00149     return local_name;
00150 }
00151 
00152 /* Comparing protocol */
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         // return asString()->sameAs(fn->asString());
00166         return asString()->asUppercase()->isEqual(fn->asString()->asUppercase());
00167     }
00168 }
00169 
00170 /* Converting protocol */
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 /* File utilities protocol */
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 /* Parsing protocol */
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 /* Printing protocol */
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 /* Stream creation protocol */
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 /* Testing protocol */
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 /* Private protocol */
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 }

Generated on Mon Nov 27 09:47:55 2006 for Smalltalk like C++ Class Library by  doxygen 1.4.2