Serenity Operating System
at master 254 lines 7.4 kB view raw
1/* 2 * Copyright (c) 2021, János Tóth <toth-janos@outlook.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/DeprecatedString.h> 8#include <AK/Optional.h> 9#include <AK/Vector.h> 10#include <LibMain/Main.h> 11#include <ctype.h> 12#include <fcntl.h> 13#include <stdint.h> 14#include <stdio.h> 15#include <sys/types.h> 16#include <unistd.h> 17 18char const* usage = "usage:\n" 19 "\tdd <options>\n" 20 "options:\n" 21 "\tif=<file>\tinput file (default: stdin)\n" 22 "\tof=<file>\toutput file (default: stdout)\n" 23 "\tbs=<size>\tblocks size may be followed by multiplicate suffixes: k=1024, M=1024*1024, G=1024*1024*1024 (default: 512)\n" 24 "\tcount=<size>\t<size> blocks to copy (default: 0 (until end-of-file))\n" 25 "\tseek=<size>\tskip <size> blocks at start of output (default: 0)\n" 26 "\tskip=<size>\tskip <size> blocks at start of input (default: 0)\n" 27 "\tstatus=<level>\tlevel of output (default: default)\n" 28 "\t\t\tdefault - error messages + final statistics\n" 29 "\t\t\tnone - just error messages\n" 30 "\t\t\tnoxfer - no final statistics\n" 31 "\t--help\t\tshows this text\n"; 32 33enum Status { 34 Default, 35 None, 36 Noxfer 37}; 38 39static StringView split_at_equals(StringView argument) 40{ 41 auto values = argument.split_view('=', SplitBehavior::KeepEmpty); 42 if (values.size() < 2) { 43 warnln("Unable to parse: {}", argument); 44 return {}; 45 } else { 46 return values[1]; 47 } 48} 49 50static int handle_io_file_arguments(int& fd, int flags, StringView argument) 51{ 52 auto value = split_at_equals(argument); 53 if (value.is_empty()) { 54 return -1; 55 } 56 57 fd = open(value.to_deprecated_string().characters(), flags, 0666); 58 if (fd == -1) { 59 warnln("Unable to open: {}", value); 60 return -1; 61 } else { 62 return 0; 63 } 64} 65 66static int handle_size_arguments(size_t& numeric_value, StringView argument) 67{ 68 auto value = split_at_equals(argument); 69 if (value.is_empty()) { 70 return -1; 71 } 72 73 unsigned suffix_multiplier = 1; 74 auto suffix = value[value.length() - 1]; 75 switch (tolower(suffix)) { 76 case 'k': 77 suffix_multiplier = KiB; 78 value = value.substring_view(0, value.length() - 1); 79 break; 80 case 'm': 81 suffix_multiplier = MiB; 82 value = value.substring_view(0, value.length() - 1); 83 break; 84 case 'g': 85 suffix_multiplier = GiB; 86 value = value.substring_view(0, value.length() - 1); 87 break; 88 } 89 90 Optional<unsigned> numeric_optional = value.to_uint(); 91 if (!numeric_optional.has_value()) { 92 warnln("Invalid size-value: {}", value); 93 return -1; 94 } 95 96 numeric_value = numeric_optional.value() * suffix_multiplier; 97 if (numeric_value < 1) { 98 warnln("Invalid size-value: {}", numeric_value); 99 return -1; 100 } else { 101 return 0; 102 } 103} 104 105static int handle_status_arguments(Status& status, StringView argument) 106{ 107 auto value = split_at_equals(argument); 108 if (value.is_empty()) { 109 return -1; 110 } 111 112 if (value == "default") { 113 status = Default; 114 return 0; 115 } else if (value == "noxfer") { 116 status = Noxfer; 117 return 0; 118 } else if (value == "none") { 119 status = None; 120 return 0; 121 } else { 122 warnln("Unknown status: {}", value); 123 return -1; 124 } 125} 126 127ErrorOr<int> serenity_main(Main::Arguments arguments) 128{ 129 int input_fd = 0; 130 int input_flags = O_RDONLY; 131 int output_fd = 1; 132 int output_flags = O_CREAT | O_WRONLY | O_TRUNC; 133 size_t block_size = 512; 134 size_t count = 0; 135 size_t skip = 0; 136 size_t seek = 0; 137 Status status = Default; 138 139 size_t total_bytes_copied = 0; 140 size_t total_blocks_in = 0, partial_blocks_in = 0; 141 size_t total_blocks_out = 0, partial_blocks_out = 0; 142 uint8_t* buffer = nullptr; 143 ssize_t nread = 0, nwritten = 0; 144 145 for (size_t a = 1; a < arguments.strings.size(); a++) { 146 auto argument = arguments.strings[a]; 147 148 if (argument == "--help") { 149 out("{}", usage); 150 return 0; 151 } else if (argument.starts_with("if="sv)) { 152 if (handle_io_file_arguments(input_fd, input_flags, argument) < 0) { 153 return 1; 154 } 155 } else if (argument.starts_with("of="sv)) { 156 if (handle_io_file_arguments(output_fd, output_flags, argument) < 0) { 157 return 1; 158 } 159 } else if (argument.starts_with("bs="sv)) { 160 if (handle_size_arguments(block_size, argument) < 0) { 161 return 1; 162 } 163 } else if (argument.starts_with("count="sv)) { 164 if (handle_size_arguments(count, argument) < 0) { 165 return 1; 166 } 167 } else if (argument.starts_with("seek="sv)) { 168 if (handle_size_arguments(seek, argument) < 0) { 169 return 1; 170 } 171 } else if (argument.starts_with("skip="sv)) { 172 if (handle_size_arguments(skip, argument) < 0) { 173 return 1; 174 } 175 } else if (argument.starts_with("status="sv)) { 176 if (handle_status_arguments(status, argument) < 0) { 177 return 1; 178 } 179 } else { 180 warn("{}", usage); 181 return 1; 182 } 183 } 184 185 if ((buffer = (uint8_t*)malloc(block_size)) == nullptr) { 186 warnln("Unable to allocate {} bytes for the buffer.", block_size); 187 return -1; 188 } 189 190 if (seek > 0) { 191 if (lseek(output_fd, seek * block_size, SEEK_SET) < 0) { 192 warnln("Unable to seek {} bytes.", seek * block_size); 193 return -1; 194 } 195 } 196 197 while (1) { 198 nread = read(input_fd, buffer, block_size); 199 if (nread < 0) { 200 warnln("Cannot read from the input."); 201 break; 202 } else if (nread == 0) { 203 break; 204 } else { 205 if ((size_t)nread != block_size) { 206 partial_blocks_in++; 207 } else { 208 total_blocks_in++; 209 } 210 211 if (partial_blocks_in + total_blocks_in <= skip) { 212 continue; 213 } 214 215 nwritten = write(output_fd, buffer, nread); 216 if (nwritten < 0) { 217 warnln("Cannot write to the output."); 218 break; 219 } else if (nwritten == 0) { 220 break; 221 } else { 222 if ((size_t)nwritten < block_size) { 223 partial_blocks_out++; 224 } else { 225 total_blocks_out++; 226 } 227 228 total_bytes_copied += nwritten; 229 230 if (count > 0 && (partial_blocks_out + total_blocks_out) >= count) { 231 break; 232 } 233 } 234 } 235 } 236 237 if (status == Default) { 238 warnln("{}+{} blocks in", total_blocks_in, partial_blocks_in); 239 warnln("{}+{} blocks out", total_blocks_out, partial_blocks_out); 240 warnln("{} bytes copied.", total_bytes_copied); 241 } 242 243 free(buffer); 244 245 if (input_fd != 0) { 246 close(input_fd); 247 } 248 249 if (output_fd != 1) { 250 close(output_fd); 251 } 252 253 return 0; 254}