Browse Source

tail: Implement byte mode into `tail`

This works exactly the same as with lines but with bytes instead.
Fabian Dellwing 1 year ago
parent
commit
e511c553a8
1 changed files with 63 additions and 1 deletions
  1. 63 1
      Userland/Utilities/tail.cpp

+ 63 - 1
Userland/Utilities/tail.cpp

@@ -83,6 +83,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     TRY(Core::System::pledge("stdio rpath"));
     TRY(Core::System::pledge("stdio rpath"));
 
 
     bool follow = false;
     bool follow = false;
+    size_t wanted_byte_count = 0;
+    bool byte_mode = false;
     size_t wanted_line_count = DEFAULT_LINE_COUNT;
     size_t wanted_line_count = DEFAULT_LINE_COUNT;
     bool start_from_end = true;
     bool start_from_end = true;
     StringView file;
     StringView file;
@@ -114,6 +116,30 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
             return true;
             return true;
         },
         },
     });
     });
+    args_parser.add_option(Core::ArgsParser::Option {
+        .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
+        .help_string = "output the last NUM bytes; or use -c +NUM to"
+                       " output starting with byte NUM",
+        .long_name = "bytes",
+        .short_name = 'c',
+        .value_name = "[+]NUM",
+        .accept_value = [&](StringView bytes) -> ErrorOr<bool> {
+            Optional<size_t> value;
+            if (bytes.starts_with('+')) {
+                value = bytes.substring_view(1, bytes.length() - 1).to_number<size_t>();
+                start_from_end = false;
+            } else {
+                value = bytes.to_number<size_t>();
+            }
+            if (!value.has_value()) {
+                warnln("Invalid number: {}", bytes);
+                return false;
+            }
+            wanted_byte_count = value.value();
+            byte_mode = true;
+            return true;
+        },
+    });
     args_parser.add_positional_argument(file, "File path", "file", Core::ArgsParser::Required::No);
     args_parser.add_positional_argument(file, "File path", "file", Core::ArgsParser::Required::No);
     args_parser.parse(arguments);
     args_parser.parse(arguments);
 
 
@@ -135,6 +161,26 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
             size_t line_index = 0;
             size_t line_index = 0;
             StringBuilder line;
             StringBuilder line;
 
 
+            if (byte_mode) {
+                if (start_from_end) {
+                    if (wanted_byte_count > bytes.size()) {
+                        out("{}", StringView { bytes });
+                        continue;
+                    }
+                    out("{}", StringView { bytes }.substring_view(bytes.size() - wanted_byte_count, wanted_byte_count));
+                    continue;
+                }
+
+                if (wanted_byte_count > bytes.size())
+                    continue;
+
+                if (wanted_byte_count > 0)
+                    out("{}", StringView { bytes }.substring_view(wanted_byte_count - 1, bytes.size() - wanted_byte_count + 1));
+                else
+                    out("{}", StringView { bytes });
+                continue;
+            }
+
             if (!line_count && wanted_line_count) {
             if (!line_count && wanted_line_count) {
                 out("{}", StringView { bytes });
                 out("{}", StringView { bytes });
                 continue;
                 continue;
@@ -180,7 +226,23 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         return 0;
         return 0;
     }
     }
 
 
-    auto pos = TRY(find_seek_pos(*f, wanted_line_count, start_from_end));
+    off_t pos = 0;
+    if (byte_mode) {
+        auto file_size = TRY(f->size());
+        if (wanted_byte_count > file_size)
+            wanted_byte_count = file_size;
+        if (start_from_end) {
+            TRY(f->seek(wanted_byte_count * -1, SeekMode::FromEndPosition));
+        } else {
+            if (wanted_byte_count > 0 && wanted_byte_count < file_size)
+                TRY(f->seek(wanted_byte_count - 1, SeekMode::SetPosition));
+            else
+                TRY(f->seek(0, SeekMode::FromEndPosition));
+        }
+        pos = TRY(f->tell());
+    } else {
+        pos = TRY(find_seek_pos(*f, wanted_line_count, start_from_end));
+    }
     TRY(tail_from_pos(*f, pos));
     TRY(tail_from_pos(*f, pos));
 
 
     if (follow) {
     if (follow) {