00001 #ifndef FILENAME_IMPLEMENTATION_FILE
00002 #define FILENAME_IMPLEMENTATION_FILE
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "byte_filer.h"
00022 #include "filename.h"
00023
00024 #include <basis/byte_array.h>
00025 #include <basis/function.h>
00026 #include <basis/istring.h>
00027 #include <basis/log_base.h>
00028 #include <basis/portable.h>
00029 #include <basis/string_array.h>
00030
00031 #include <stdio.h>
00032 #include <sys/stat.h>
00033 #include <sys/types.h>
00034 #ifdef __WIN32__
00035 #include <io.h>
00036 #endif
00037
00038 #if defined(__WIN32__) || defined(__VMS__)
00039 const char DEFAULT_SEPARATOR = '\\';
00040 #elif defined(__UNIX__)
00041 const char DEFAULT_SEPARATOR = '/';
00042 #else
00043 #error "We have no idea what the default path separator is."
00044 #endif
00045
00046 const char *NO_PARENT_DEFAULT = ".";
00047
00048
00049 filename::filename()
00050 : _had_directory(false),
00051 _contents(new istring)
00052 {}
00053
00054 filename::filename(const istring &name)
00055 : _had_directory(true),
00056 _contents(new istring(name))
00057 { canonicalize(); }
00058
00059 filename::filename(const istring &directory, const istring &name_of_file)
00060 : _had_directory(true),
00061 _contents(new istring(directory))
00062 {
00063
00064 if (!directory) {
00065 *_contents = istring(NO_PARENT_DEFAULT);
00066 _had_directory = false;
00067 }
00068
00069
00070 bool add_slash = false;
00071 if ( (directory[directory.end()] != '\\')
00072 && (directory[directory.end()] != '/') ) add_slash = true;
00073 if (add_slash) *_contents += DEFAULT_SEPARATOR;
00074 *_contents += name_of_file;
00075 canonicalize();
00076 }
00077
00078 filename::filename(const filename &to_copy)
00079 : packable(),
00080 _had_directory(to_copy._had_directory),
00081 _contents(new istring(*to_copy._contents))
00082 { canonicalize(); }
00083
00084 filename::~filename() { WHACK(_contents); }
00085
00086 istring filename::default_separator() { return istring(DEFAULT_SEPARATOR, 1); }
00087
00088 filename::operator const istring &() const { return *_contents; }
00089
00090 istring &filename::raw() { return *_contents; }
00091
00092 const istring &filename::raw() const { return *_contents; }
00093
00094 bool filename::good() const { return exists(); }
00095
00096 bool filename::unlink() const { return ::unlink(_contents->s()) == 0; }
00097
00098 bool filename::separator(char is_it)
00099 { return (is_it == pc_separator) || (is_it == unix_separator); }
00100
00101 filename &filename::operator = (const filename &to_copy)
00102 {
00103 if (this == &to_copy)
00104 return *this;
00105 *_contents = *to_copy._contents;
00106 _had_directory = to_copy._had_directory;
00107 return *this;
00108 }
00109
00110 filename &filename::operator = (const istring &to_copy)
00111 {
00112 _had_directory = true;
00113 *_contents = to_copy;
00114 canonicalize();
00115 return *this;
00116 }
00117
00118 bool filename::operator == (const filename &that) const
00119 { return *_contents == *that._contents; }
00120
00121 istring filename::pop()
00122 {
00123 istring to_return = basename();
00124 filename parent_dir = parent();
00125 if (parent_dir.raw() == NO_PARENT_DEFAULT) {
00126
00127 return "";
00128 }
00129 *this = parent_dir;
00130 return to_return;
00131 }
00132
00133 filename filename::parent() const { return dirname(); }
00134
00135 void filename::push(const istring &to_push)
00136 {
00137 *this = filename(*this, to_push);
00138 }
00139
00140 void filename::canonicalize()
00141 {
00142
00143 bool found_sep = false;
00144 for (int j = 0; j < _contents->length(); j++) {
00145 if (separator(_contents->get(j))) {
00146 found_sep = true;
00147 _contents->put(j, DEFAULT_SEPARATOR);
00148 }
00149 }
00150
00151
00152
00153
00154 if (!found_sep) _had_directory = false;
00155
00156
00157
00158
00159 bool saw_sep = false;
00160 for (int i = 1; i < _contents->length(); i++) {
00161 if (separator(_contents->get(i))) {
00162 if (saw_sep) {
00163 _contents->zap(i, i);
00164
00165 i--;
00166 continue;
00167 }
00168 saw_sep = true;
00169 } else saw_sep = false;
00170 }
00171
00172
00173
00174 if (_contents->length() > 3) {
00175
00176 const int last = _contents->end();
00177 if (separator(_contents->get(last))) _contents->zap(last, last);
00178 } else if ( (_contents->length() == 2) && (_contents->get(1) == ':') ) {
00179
00180
00181
00182 *_contents += istring(DEFAULT_SEPARATOR, 1);
00183 }
00184 }
00185
00186 char filename::drive(bool interact_with_fs) const
00187 {
00188
00189 if (_contents->length() < 2)
00190 return '\0';
00191 if (_contents->get(1) == ':')
00192 return _contents->get(0);
00193 if (!interact_with_fs)
00194 return '\0';
00195
00196
00197 status_info fill;
00198 if (!get_info(fill))
00199 return '\0';
00200 return char('A' + fill.st_dev);
00201 }
00202
00203 istring filename::extension() const
00204 {
00205 istring base(basename().raw());
00206 int posn = base.find('.', base.end(), true);
00207 if (negative(posn))
00208 return "";
00209 return base.substring(posn + 1, base.length() - 1);
00210 }
00211
00212 istring filename::rootname() const
00213 {
00214 istring base(basename().raw());
00215 int posn = base.find('.', base.end(), true);
00216 if (negative(posn))
00217 return base;
00218 return base.substring(0, posn - 1);
00219 }
00220
00221 bool filename::get_info(status_info &to_fill) const
00222 {
00223 int ret = stat(_contents->observe(), &to_fill);
00224 if (ret)
00225 return false;
00226 return true;
00227 }
00228
00229 bool filename::is_directory() const
00230 {
00231 status_info fill;
00232 if (!get_info(fill))
00233 return false;
00234 return !!(fill.st_mode & S_IFDIR);
00235 }
00236
00237 bool filename::is_writable() const
00238 {
00239 status_info fill;
00240 if (!get_info(fill))
00241 return false;
00242 return !!(fill.st_mode & S_IWRITE);
00243 }
00244
00245 bool filename::is_readable() const
00246 {
00247 status_info fill;
00248 if (!get_info(fill))
00249 return false;
00250 return !!(fill.st_mode & S_IREAD);
00251 }
00252
00253 bool filename::is_executable() const
00254 {
00255 status_info fill;
00256 if (!get_info(fill))
00257 return false;
00258 return !!(fill.st_mode & S_IEXEC);
00259 }
00260
00261 int filename::find_last_separator(const istring &look_at) const
00262 {
00263 int last_sep = -1;
00264 int sep = 0;
00265 while (sep >= 0) {
00266 sep = look_at.find(DEFAULT_SEPARATOR, last_sep + 1);
00267 if (sep >= 0) last_sep = sep;
00268 }
00269 return last_sep;
00270 }
00271
00272 filename filename::basename() const
00273 {
00274 istring basename = *_contents;
00275 int last_sep = find_last_separator(basename);
00276 if (last_sep >= 0) basename.zap(0, last_sep);
00277 return basename;
00278 }
00279
00280 filename filename::dirname() const
00281 {
00282 istring dirname = *_contents;
00283 int last_sep = find_last_separator(dirname);
00284
00285 if (last_sep >= 1) {
00286
00287
00288
00289 dirname.zap(last_sep, dirname.end());
00290 } else {
00291 if (_contents->get(0) == DEFAULT_SEPARATOR) {
00292
00293
00294
00295 dirname = istring(DEFAULT_SEPARATOR, 1);
00296 } else {
00297
00298
00299
00300 dirname = NO_PARENT_DEFAULT;
00301 }
00302 }
00303 return dirname;
00304 }
00305
00306 istring filename::dirname(bool add_slash) const
00307 {
00308 istring tempname = dirname().raw();
00309 if (add_slash) tempname += DEFAULT_SEPARATOR;
00310 return tempname;
00311 }
00312
00313 bool filename::exists() const
00314 {
00315 if (is_directory())
00316 return true;
00317 if (!_contents->length())
00318 return false;
00319 byte_filer opened(_contents->observe(), "rb");
00320 return opened.good();
00321 }
00322
00323 bool filename::legal_character(char to_check)
00324 {
00325 switch (to_check) {
00326 case ':': case ';':
00327 case '\\': case '/':
00328 case '*': case '?': case '$': case '&': case '|':
00329 case '\'': case '"': case '`':
00330 case '(': case ')':
00331 case '[': case ']':
00332 case '<': case '>':
00333 case '{': case '}':
00334 return false;
00335 default: return true;
00336 }
00337 }
00338
00339 void filename::detooth_filename(istring &to_clean, char replacement)
00340 {
00341 for (int i = 0; i < to_clean.length(); i++) {
00342 if (!legal_character(to_clean[i]))
00343 to_clean[i] = replacement;
00344 }
00345 }
00346
00347 void filename::pack(byte_array &packed_form) const
00348 {
00349 basis::attach(packed_form, int(_had_directory));
00350 _contents->pack(packed_form);
00351 }
00352
00353 bool filename::unpack(byte_array &packed_form)
00354 {
00355 int temp;
00356 if (!basis::detach(packed_form, temp))
00357 return false;
00358 _had_directory = temp;
00359 if (!_contents->unpack(packed_form))
00360 return false;
00361 return true;
00362 }
00363
00364 void filename::separate(string_array &pieces) const
00365 {
00366 pieces.reset();
00367 const istring &raw_form = raw();
00368 istring accumulator;
00369 for (int i = 0; i < raw_form.length(); i++) {
00370 if (separator(raw_form[i])) {
00371
00372
00373 if (!i || accumulator.length()) pieces += accumulator;
00374
00375 accumulator = istring::empty_string();
00376 } else {
00377
00378 accumulator += raw_form[i];
00379 }
00380 }
00381 if (accumulator.length()) pieces += accumulator;
00382 }
00383
00384 void filename::join(const string_array &pieces)
00385 {
00386 istring constructed_name;
00387 for (int i = 0; i < pieces.length(); i++) {
00388 constructed_name += pieces[i];
00389 if (!i || (i != pieces.length() - 1))
00390 constructed_name += DEFAULT_SEPARATOR;
00391 }
00392 *this = constructed_name;
00393 }
00394
00395 bool filename::base_compare_prefix(const filename &to_compare,
00396 string_array &first, string_array &second)
00397 {
00398 separate(first);
00399 to_compare.separate(second);
00400
00401
00402 if (first.length() > second.length())
00403 return false;
00404
00405
00406 for (int i = 0; i < first.length(); i++) {
00407 #if defined(__WIN32__) || defined(__VMS__)
00408
00409 if (!first[i].iequals(second[i]))
00410 return false;
00411 #else
00412
00413 if (first[i] != second[i])
00414 return false;
00415 #endif
00416 }
00417 return true;
00418 }
00419
00420 bool filename::compare_prefix(const filename &to_compare, istring &sequel)
00421 {
00422 sequel = istring::empty_string();
00423 string_array first;
00424 string_array second;
00425 if (!base_compare_prefix(to_compare, first, second))
00426 return false;
00427
00428
00429 int extra_strings = second.length() - first.length();
00430 for (int i = second.length() - extra_strings; i < second.length(); i++) {
00431 sequel += second[i];
00432 if (i != second.length() - 1) sequel += DEFAULT_SEPARATOR;
00433 }
00434
00435 return true;
00436 }
00437
00438 bool filename::compare_prefix(const filename &to_compare)
00439 {
00440 string_array first;
00441 string_array second;
00442 return base_compare_prefix(to_compare, first, second);
00443 }
00444
00445 bool filename::base_compare_suffix(const filename &to_compare,
00446 string_array &first, string_array &second)
00447 {
00448 separate(first);
00449 to_compare.separate(second);
00450
00451
00452 if (first.length() > second.length())
00453 return false;
00454
00455
00456 for (int i = first.length() - 1; i >= 0; i--) {
00457
00458 int distance_from_end = first.length() - 1 - i;
00459 int j = second.length() - 1 - distance_from_end;
00460 #if defined(__WIN32__) || defined(__VMS__)
00461
00462 if (!first[i].iequals(second[j]))
00463 return false;
00464 #else
00465
00466 if (first[i] != second[j])
00467 return false;
00468 #endif
00469 }
00470 return true;
00471 }
00472
00473 bool filename::compare_suffix(const filename &to_compare, istring &prequel)
00474 {
00475 prequel = istring::empty_string();
00476 string_array first;
00477 string_array second;
00478 if (!base_compare_suffix(to_compare, first, second))
00479 return false;
00480
00481
00482 int extra_strings = second.length() - first.length();
00483 for (int i = 0; i < extra_strings; i++) {
00484 prequel += second[i];
00485 if (i != second.length() - 1) prequel += DEFAULT_SEPARATOR;
00486 }
00487 return true;
00488 }
00489
00490 bool filename::compare_suffix(const filename &to_compare)
00491 {
00492 string_array first;
00493 string_array second;
00494 return base_compare_suffix(to_compare, first, second);
00495 }
00496
00497 bool filename::chmod(int write_mode, int owner_mode) const
00498 {
00499 int chmod_value;
00500 #ifdef __UNIX__
00501 if (write_mode & ALLOW_READ) {
00502 if (owner_mode & USER_RIGHTS) chmod_value |= S_IRUSR;
00503 if (owner_mode & GROUP_RIGHTS) chmod_value |= S_IRGRP;
00504 if (owner_mode & OTHER_RIGHTS) chmod_value |= S_IROTH;
00505 }
00506 if (write_mode & ALLOW_WRITE) {
00507 if (owner_mode & USER_RIGHTS) chmod_value |= S_IWUSR;
00508 if (owner_mode & GROUP_RIGHTS) chmod_value |= S_IWGRP;
00509 if (owner_mode & OTHER_RIGHTS) chmod_value |= S_IWOTH;
00510 }
00512 #elif defined(__WIN32__)
00513 if (write_mode & ALLOW_READ) {
00514 chmod_value |= _S_IREAD;
00515 }
00516 if (write_mode & ALLOW_WRITE) {
00517 chmod_value |= _S_IWRITE;
00518 }
00519 #else
00520 #error unsupported OS type currently.
00521 #endif
00522 int chmod_result = ::chmod(raw().s(), chmod_value);
00523 if (chmod_result) {
00524
00525 return false;
00526 }
00527 return true;
00528 }
00529
00530
00531 #endif //FILENAME_IMPLEMENTATION_FILE
00532