// Set this if you don't want MIME types //#define NO_MIME #include #include #include #include #include #include #ifndef NO_MIME #include "mime.h" #include "archives.h" // Run make nomime #endif #include #include #include #include #include #include #include "devinfo.c" void ListPartitions(const std::string& device) { std::ifstream file("/proc/partitions"); std::string line; while (std::getline(file, line)) { if (line.find(device) != std::string::npos) { std::cout << line << std::endl; } } } enum FileType { Unknown, File, Folder, BLKFile, CHRFile, FIFO, Symlink, Socket, #ifndef NO_MIME Archive, Audio, Application, Video, Binary, Text #endif }; std::array get_files_in_directory(const std::string& path) { DIR* dp = opendir(path.c_str()); // int normal, hidden; std::array normal = {0, 0}; std::array hidden = {0, 0}; if (dp != nullptr) { struct dirent* ep; while((ep = readdir(dp))) { // if (ep->d_name[0] != '.') i++; if (strcmp(ep->d_name, ".") != 0 && strcmp(ep->d_name, "..") != 0) { if (ep->d_name[0] == '.') { hidden[(ep->d_type == DT_DIR) ? 1 : 0]++; }else { normal[(ep->d_type == DT_DIR) ? 1 : 0]++; } } } (void)closedir(dp); } else perror("Couldn't open the directory"); std::array result; result[0] = normal[0]; result[1] = normal[1]; result[2] = hidden[0]; result[3] = hidden[1]; return result; } bool starts_with(const std::string& prefix, const std::string& string) { return string.rfind(prefix, 0) == 0; } #ifndef NO_MIME bool is_archive(const std::string& mime_type) { const std::string archives[] = ARCHIVES; int len = sizeof(archives)/sizeof(archives[0]); for(int i = 0; i < len; ++i) { if(starts_with(archives[i], mime_type)) { return true; } } return false; } #endif std::string get_name_from_filetype(const enum FileType fs) { switch (fs) { #ifndef NO_MIME case FileType::Application: return "Application"; case FileType::Archive: return "Archive"; case FileType::Audio: return "Audio"; case FileType::Binary: return "Binary"; #endif case FileType::File: return "File"; case FileType::Folder: return "Folder"; case FileType::BLKFile: return "Block special file"; case FileType::CHRFile: return "Character special file"; case FileType::FIFO: return "Pipe or FIFO special file"; case FileType::Socket: return "UNIX socket"; case FileType::Symlink: return "Symbolic"; #ifndef NO_MIME case FileType::Text: return "Text"; case FileType::Video: return "Video"; #endif default: return "Unknown"; } } std::string get_emoji_from_filetype(const enum FileType fs) { #ifndef NO_EMOJIS switch (fs) { #ifndef NO_MIME case FileType::Application: return "⚙️"; case FileType::Archive: return "🗜️"; case FileType::Audio: return "🎵"; case FileType::Binary: return "⚙️"; #endif case FileType::File: return "📄"; case FileType::Folder: return "📁"; case FileType::BLKFile: return "🪄"; case FileType::CHRFile: return "🖨️"; case FileType::FIFO: return "🤝"; case FileType::Socket: return "🔌"; case FileType::Symlink: return "🔗"; #ifndef NO_MIME case FileType::Text: return "📝"; case FileType::Video: return "🎥"; #endif default: return "?"; } #else return ""; #endif } std::string readable_fs(const long int size /*in bytes*/, const double divide_by = 1024, const std::string& suffix = "B") { if(size < 1024) { char buffer[128]; sprintf(buffer, "%i B", static_cast(size)); std::string result(buffer); return buffer; } auto result = static_cast(size); int i = 0; const std::string units[] = {"", "K", "M", "G", "T", "P", "E", "Z", "Y"}; while (result > divide_by) { result /= divide_by; i++; } char buffer[128]; sprintf(buffer, "%.*f %s%s", i, result, units[i].c_str(), suffix.c_str()); std::string str_result(buffer); return str_result; } #ifndef NO_MIME std::string get_mime(const std::string& filename) { const char* file_path = filename.c_str(); const auto magic_cookie = static_cast(magic_open(MAGIC_MIME_TYPE)); if (magic_cookie == nullptr) { std::cerr << "Unable to initialize libmagic\n"; return ""; } // Load definitions from the mime types database if (magic_load(magic_cookie, nullptr) != 0) { std::cerr << "Unable to load database definitions\n"; magic_close(magic_cookie); return ""; } // Determines the MIME type of the file const char *mime_type = magic_file(magic_cookie, file_path); if (mime_type == nullptr) { std::cerr << "Unable to determine the MIME type of the file\n"; magic_close(magic_cookie); return ""; } std::string result(mime_type); // Close libmagic magic_close(magic_cookie); return result; } #endif std::string print_permissions(mode_t mode, bool is_dir) { std::string user; user += (mode & S_IRUSR) ? "r" : "-"; user += (mode & S_IWUSR) ? "w" : "-", user += (mode & S_IXUSR) ? "x" : "-"; std::string group; group += (mode & S_IRGRP) ? "r" : "-"; group += (mode & S_IWGRP) ? "w" : "-"; group += (mode & S_IXGRP) ? "x" : "-"; std::string other; other += (mode & S_IROTH) ? "r" : "-"; other += (mode & S_IWOTH) ? "w" : "-"; other += (mode & S_IXOTH) ? "x" : "-"; return (is_dir ? "d" : "-") + user + group + other; } std::string basename(const std::string& filepath) { return filepath.substr(filepath.find_last_of("/\\") + 1); } FileType getFileType(mode_t st_mode) { if(S_ISBLK(st_mode)) return FileType::BLKFile; if(S_ISCHR(st_mode)) return FileType::CHRFile; if(S_ISDIR(st_mode)) return FileType::Folder; if(S_ISFIFO(st_mode)) return FileType::FIFO; if(S_ISLNK(st_mode)) return FileType::Symlink; if(S_ISSOCK(st_mode)) return FileType::Socket; if(S_ISREG(st_mode)) return FileType::File; } int main(int argc, char *argv[]) { bool absolute = false; #ifndef NO_EMOJIS bool emojis = true; #else bool emojis = false; #endif if (argc < 2) { std::cerr << "Usage:\n\t" << argv[0] << " [args] filename ...\n"; std::cerr << "Args:" << std::endl; std::cerr << "\t-A Output absolute path" << std::endl; std::cerr << "\t-E Do not show emojis" << std::endl; return 255; } std::vector files; for (int i = 1; i < argc; ++i) { if (argv[i][0] != '-') { std::string temp(argv[i]); files.insert(files.end(), temp); } else { std::string arg(argv[i]); if(arg == "-A") { absolute = true; } if (arg == "-E") { emojis = false; } } } size_t last = files.size(); for (size_t i = 0; i < last; ++i) { const std::string& filename = files[i]; char* c_filename = const_cast(filename.c_str()); struct stat file_stat = {}; if (stat(filename.c_str(), &file_stat) != 0) { perror("stat"); return 1; } // start FileType file = getFileType(file_stat.st_mode); bool is_file = file == FileType::File; char temp[4097] = {'\0'}; if (realpath(c_filename, temp) == nullptr) { perror("realpath"); return 1; } std::string full_name(temp); // = malloc(4097); // delete[] temp; std::string name = basename(full_name); if(file == FileType::Folder) name += "/"; #ifndef NO_MIME std::string mime_type = get_mime(full_name); //file = (is_file) ? FileType::File : FileType::Folder; #else std::string mime_type = "application/octet-stream (no mime)"; #endif if (starts_with("inode/directory", mime_type) || !is_file) { file = FileType::Folder; } else if (is_archive(mime_type)) { file = FileType::Archive; } else if (starts_with("application/octet-stream", mime_type)) { file = FileType::Binary; } else if (starts_with("application", mime_type)) { file = FileType::Application; } else if (starts_with("audio", mime_type)) { file = FileType::Audio; } else if (starts_with("video", mime_type)) { file = FileType::Video; } else if (starts_with("text", mime_type)) { file = FileType::Text; } else { file = FileType::Binary; } std::string type = get_name_from_filetype(file); std::string emoji = get_emoji_from_filetype(file); if (emojis) printf("%s ", emoji.c_str()); printf("\033[1m%s\033[0m:\n", name.c_str()); printf("\tName: %s\n", name.c_str()); printf("\tType: %s\n", type.c_str()); if (absolute) printf("\tAbsolute path: %s\n", full_name.c_str()); #ifndef NO_MIME printf("\tMIME: %s\n", mime_type.c_str()); #endif if (is_file) { std::string _1024 = readable_fs(file_stat.st_size, 1000, "B"); std::string _1000 = readable_fs(file_stat.st_size, 1024, "iB"); if (file_stat.st_size >= 1000) printf("\tFile size: %s or %s (%li bytes)\n", _1024.c_str(), _1000.c_str(), file_stat.st_size); else printf("\tFile size: %li bytes\n", file_stat.st_size); } else if (file == FileType::Folder) { auto insides = get_files_in_directory(full_name); printf("\tContents:\n"); printf("\t\tNormal: %i files and %i folders.\n", insides[0], insides[1]); printf("\t\tHidden: %i files and %i folders.\n", insides[2], insides[3]); } else if (file == FileType::CHRFile) { ListPartitions(full_name); } printf("\tPermissions: %s\n", print_permissions(file_stat.st_mode, !is_file).c_str()); if (i != last-1) { printf("\n"); } } return 0; }