Add io(1).

This commit is contained in:
Jonas 'Sortie' Termansen 2016-02-07 16:27:43 +01:00
parent 8bc1e6a2c0
commit 0344dbb3ee
1 changed files with 163 additions and 0 deletions

163
utils/io.c Normal file
View File

@ -0,0 +1,163 @@
#include <sys/stat.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
static uintmax_t parse_amount(const char *string, blksize_t blksize)
{
const char *end;
errno = 0;
uintmax_t value = strtoumax(string, (char **)&end, 10);
if (errno == ERANGE)
errx(1, "argument overflow: %s", string);
if (*end) {
uintmax_t magnitude = 1;
unsigned char magc = tolower((unsigned char)*end);
switch (magc) {
case 'k': magnitude = UINTMAX_C(1024) << 0; break;
case 'm': magnitude = UINTMAX_C(1024) << 1; break;
case 'g': magnitude = UINTMAX_C(1024) << 2; break;
case 't': magnitude = UINTMAX_C(1024) << 3; break;
case 'p': magnitude = UINTMAX_C(1024) << 4; break;
case 'e': magnitude = UINTMAX_C(1024) << 5; break;
case 'z': magnitude = UINTMAX_C(1024) << 6; break;
case 'y': magnitude = UINTMAX_C(1024) << 7; break;
case 'x': magnitude = blksize; break;
default: errx(1, "unsupported unit: %s", end);
}
if (magc != 'x' || !(strcasecmp(end + 1, "B") == 0 ||
strcasecmp(end + 1, "iB ")) )
errx(1, "unsupported unit: %s", end);
if (UINTMAX_MAX / magnitude < value)
errx(1, "argument overflow: %s", string);
value *= magnitude;
}
return value;
}
static size_t parse_size_t(const char *string, blksize_t blksize)
{
uintmax_t result = parse_amount(string, blksize);
if (result != (size_t) result)
errx(1, "argument overflow: %s", string);
return result;
}
int main(int argc, char *argv[])
{
int input_fd = 0;
int output_fd = 1;
const char *input_path = NULL;
const char *output_path = NULL;
const char *block_size_str = NULL;
off_t count = -1;
const char *count_str = NULL;
int opt;
while ((opt = getopt(argc, argv, "b:c:i:I:o:O:q:s:S:v:")) != -1) {
switch (opt) {
case 'b': block_size_str = optarg; break;
case 'c': count_str = optarg; break;
case 'i': input_path = optarg; break;
case 'I': break;
case 'o': output_path = optarg; break;
case 'O': break;
case 'q': break;
case 's': break;
case 'S': break;
case 'v': break;
default: break;
}
}
if (optind < argc)
errx(1, "unexpected extra operand");
if (input_path) {
input_fd = open(input_path, O_RDONLY);
if (input_fd < 0)
err(1, "%s", input_path);
}
else
input_path = "<stdin>";
if (output_path) {
int flags = O_WRONLY | O_CREAT | O_TRUNC;
output_fd = open(output_path, flags, 0666);
if (output_fd < 0)
err(1, "%s", input_path);
}
else
output_path = "<stdout>";
struct stat input_st;
if (fstat(input_fd, &input_st) < 0)
err(1, "stat: %s", input_path);
struct stat output_st;
if (fstat(output_fd, &output_st) < 0)
err(1, "stat: %s", output_path);
blksize_t blksize = input_st.st_blksize < output_st.st_blksize ?
input_st.st_blksize : output_st.st_blksize;
if (blksize == 0)
blksize = 512;
size_t block_size = 0;
if (block_size_str)
block_size = parse_size_t(block_size_str, blksize);
if (block_size == 0)
block_size = blksize;
//off_t count = -1;
//if (count_str )
// count = parse_off_t(count_str, blksize);
unsigned char *block = malloc(block_size);
if (!block)
err(1, "malloc");
for (off_t blocks = 0; count == -1 || blocks < count; blocks++) {
size_t in = 0;
while (in < block_size) {
size_t left = block_size - in;
ssize_t done = read(input_fd, block + in, left);
if (done < 0)
err(1, "%s", input_path);
if (done == 0)
break;
in += done;
}
if ( in == 0 )
break;
size_t out = 0;
while (out < in) {
size_t left = in - out;
ssize_t done = write(output_fd, block + out, left);
if (done < 0)
err(1, "%s", output_path);
if (done == 0)
errx(1, "%s: %s", output_path,
"Unexpected early end of file");
out += done;
}
}
if (fsync(output_fd) < 0)
errx(1, "sync: %s", output_path);
// TODO: Does fsync actually sync the hardware? What does sync(1) do?
if (close(input_fd) < 0)
errx(1, "close: %s", input_path);
if (close(output_fd) < 0)
errx(1, "close: %s", output_path);
free(block);
return 0;
}