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