file.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /*
  2. * Copyright (c) 2021, Valtteri Koskivuori <vkoskiv@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/MappedFile.h>
  7. #include <AK/Vector.h>
  8. #include <LibCore/ArgsParser.h>
  9. #include <LibCore/FileStream.h>
  10. #include <LibCore/MimeData.h>
  11. #include <LibGfx/ImageDecoder.h>
  12. #include <stdio.h>
  13. #include <unistd.h>
  14. static Optional<String> description_only(String description, [[maybe_unused]] const String& path)
  15. {
  16. return description;
  17. }
  18. // FIXME: Ideally Gfx::ImageDecoder could tell us the image type directly.
  19. static Optional<String> image_details(const String& description, const String& path)
  20. {
  21. auto file_or_error = MappedFile::map(path);
  22. if (file_or_error.is_error())
  23. return {};
  24. auto& mapped_file = *file_or_error.value();
  25. auto image_decoder = Gfx::ImageDecoder::create((const u8*)mapped_file.data(), mapped_file.size());
  26. if (!image_decoder->is_valid())
  27. return {};
  28. return String::formatted("{}, {} x {}", description, image_decoder->width(), image_decoder->height());
  29. }
  30. #define ENUMERATE_DESCRIPTION_CONTENTS(V) \
  31. V(pbm, "image/x-portable-bitmap", "PBM image data", image_details) \
  32. V(pgm, "image/x-portable-graymap", "PGM image data", image_details) \
  33. V(png, "image/png", "PNG image data", image_details) \
  34. V(ppm, "image/x-portable-pixmap", "PPM image data", image_details) \
  35. V(gif_87, "image/gif", "GIF image data", image_details) \
  36. V(gif_89, "image/gif", "GIF image data", image_details) \
  37. V(bmp, "image/bmp", "BMP image data", image_details) \
  38. V(jpeg, "image/jpeg", "JPEG image data", image_details) \
  39. V(jpeg_jfif, "image/jpeg", "JFIF image data", image_details) \
  40. V(jpeg_huh, "image/jpeg", "JPEG image data", image_details) \
  41. V(shell, "text/x-shellscript", "POSIX shell script text executable", description_only) \
  42. V(json, "application/json", "JSON data", description_only) \
  43. V(javascript, "application/javascript", "JavaScript source", description_only) \
  44. V(markdown, "text/markdown", "Markdown document", description_only)
  45. #define V(var_name, mime_type, description, details) \
  46. static const String var_name = description;
  47. ENUMERATE_DESCRIPTION_CONTENTS(V)
  48. #undef V
  49. static Optional<String> get_description_from_mime_type(const String& mime, const String& path)
  50. {
  51. #define V(var_name, mime_type, description, details) \
  52. if (String(mime_type) == mime) \
  53. return details(String(description), path);
  54. ENUMERATE_DESCRIPTION_CONTENTS(V);
  55. #undef V
  56. return {};
  57. }
  58. int main(int argc, char** argv)
  59. {
  60. if (pledge("stdio rpath", nullptr) < 0) {
  61. perror("pledge");
  62. return 1;
  63. }
  64. Vector<const char*> paths;
  65. bool flag_mime_only = false;
  66. Core::ArgsParser args_parser;
  67. args_parser.set_general_help("Determine type of files");
  68. args_parser.add_option(flag_mime_only, "Only print mime type", "mime-type", 'I');
  69. args_parser.add_positional_argument(paths, "Files to identify", "files", Core::ArgsParser::Required::Yes);
  70. args_parser.parse(argc, argv);
  71. bool all_ok = true;
  72. for (auto path : paths) {
  73. auto file = Core::File::construct(path);
  74. if (!file->open(Core::File::ReadOnly)) {
  75. perror(path);
  76. all_ok = false;
  77. continue;
  78. }
  79. auto bytes = file->read(25);
  80. auto file_name_guess = Core::guess_mime_type_based_on_filename(path);
  81. auto mime_type = Core::guess_mime_type_based_on_sniffed_bytes(bytes.bytes()).value_or(file_name_guess);
  82. auto human_readable_description = get_description_from_mime_type(mime_type, String(path)).value_or(mime_type);
  83. outln("{}: {}", path, flag_mime_only ? mime_type : human_readable_description);
  84. }
  85. return all_ok ? 0 : 1;
  86. }