From dbd7ec5b27b7f83a4387fabe170c7f1a13b652b7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 28 Sep 2017 17:09:57 +0530 Subject: [PATCH] Allow specifying the offset and size for reading data from files Also require size to be specified for SHM objects to support platforms such as macOS that have no way to get the size from the fd. --- kitty/graphics.c | 20 +++++++++++--------- kitty/graphics.h | 2 +- kitty/parser.c | 9 +++++---- kitty_tests/graphics.py | 4 ++-- kitty_tests/parser.py | 2 +- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/kitty/graphics.c b/kitty/graphics.c index efc17e7ef..a307e4cc2 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -134,13 +134,16 @@ set_add_response(const char *code, const char *fmt, ...) { #define ABRT(code, ...) { set_add_response(#code, __VA_ARGS__); goto err; } static inline bool -mmap_img_file(GraphicsManager UNUSED *self, Image *img) { - struct stat s; - if (fstat(img->load_data.fd, &s) != 0) ABRT(EBADF, "Failed to fstat() SHM file with error: [%d] %s", errno, strerror(errno)); - void *addr = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, img->load_data.fd, 0); +mmap_img_file(GraphicsManager UNUSED *self, Image *img, size_t sz, off_t offset) { + if (!sz) { + struct stat s; + if (fstat(img->load_data.fd, &s) != 0) ABRT(EBADF, "Failed to fstat() file with error: [%d] %s", errno, strerror(errno)); + sz = s.st_size; + } + void *addr = mmap(0, sz, PROT_READ, MAP_PRIVATE, img->load_data.fd, offset); if (addr == MAP_FAILED) ABRT(EBADF, "Failed to map image file with error: [%d] %s", errno, strerror(errno)); img->load_data.mapped_file = addr; - img->load_data.mapped_file_sz = s.st_size; + img->load_data.mapped_file_sz = sz; return true; err: return false; @@ -332,11 +335,10 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ snprintf(fname, sizeof(fname)/sizeof(fname[0]), "%.*s", (int)g->payload_sz, payload); if (tt == 's') fd = shm_open(fname, O_RDONLY, 0); else fd = open(fname, O_CLOEXEC | O_RDONLY); - if (fd == -1) { - ABRT(EBADF, "Failed to open file %s for graphics transmission with error: [%d] %s", fname, errno, strerror(errno)); - } + if (fd == -1) ABRT(EBADF, "Failed to open file %s for graphics transmission with error: [%d] %s", fname, errno, strerror(errno)); img->load_data.fd = fd; - img->data_loaded = mmap_img_file(self, img); + if (tt == 's' && !g->data_sz) ABRT(EINVAL, "data size required for shared memory object"); + img->data_loaded = mmap_img_file(self, img, g->data_sz, g->data_offset); if (tt == 't') unlink(fname); else if (tt == 's') shm_unlink(fname); break; diff --git a/kitty/graphics.h b/kitty/graphics.h index 4f2da19d4..9a41c5d6e 100644 --- a/kitty/graphics.h +++ b/kitty/graphics.h @@ -9,7 +9,7 @@ typedef struct { unsigned char action, transmission_type, compressed; - uint32_t format, more, id, data_sz; + uint32_t format, more, id, data_sz, data_offset; uint32_t width, height, x_offset, y_offset, data_height, data_width, num_cells, num_lines; int32_t z_index; size_t payload_sz; diff --git a/kitty/parser.c b/kitty/parser.c index 478bf6cb2..cef74be00 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -552,6 +552,7 @@ parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) { data_height = 'v', data_width = 's', data_sz = 'S', + data_offset = 'O', num_cells = 'c', num_lines = 'r', z_index = 'z' @@ -575,7 +576,7 @@ parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) { #define KS(n, vs) case n: state = EQUAL; value_state = vs; break #define U(x) KS(x, UINT) KS(action, FLAG); KS(transmission_type, FLAG); KS(compressed, FLAG); KS(z_index, INT); - U(format); U(more); U(id); U(data_sz); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(num_cells); U(num_lines); + U(format); U(more); U(id); U(data_sz); U(data_offset); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(num_cells); U(num_lines); #undef U #undef KS default: @@ -628,7 +629,7 @@ parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) { READ_UINT; #define U(x) case x: g.x = code; break switch(key) { - U(format); U(more); U(id); U(data_sz); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(num_cells); U(num_lines); + U(format); U(more); U(id); U(data_sz); U(data_offset); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(num_cells); U(num_lines); default: break; } state = AFTER_VALUE; @@ -672,9 +673,9 @@ parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) { #define A(x) #x, g.x #define U(x) #x, (unsigned int)(g.x) #define I(x) #x, (int)(g.x) - REPORT_VA_COMMAND("s {sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI si} y#", "graphics_command", + REPORT_VA_COMMAND("s {sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI si} y#", "graphics_command", A(action), A(transmission_type), A(compressed), - U(format), U(more), U(id), U(data_sz), + U(format), U(more), U(id), U(data_sz), U(data_offset), U(width), U(height), U(x_offset), U(y_offset), U(data_height), U(data_width), U(num_cells), U(num_lines), U(payload_sz), I(z_index), payload, g.payload_sz diff --git a/kitty_tests/graphics.py b/kitty_tests/graphics.py index d684ebbd7..62830cd18 100644 --- a/kitty_tests/graphics.py +++ b/kitty_tests/graphics.py @@ -43,7 +43,7 @@ class TestGraphics(BaseTest): res = send_command(s, cmd, payload) if not res: return - return res.decode('ascii').partition(';')[2].partition(':')[0].partition('\033')[0] + return res.decode('ascii').partition(';')[2].partition('\033')[0] def sl(payload, **kw): if isinstance(payload, str): @@ -90,5 +90,5 @@ class TestGraphics(BaseTest): # Test loading from POSIX SHM name = '/kitty-test-shm' g.shm_write(name, random_data) - sl(name, s=24, v=32, t='s', expecting_data=random_data) + sl(name, s=24, v=32, t='s', S=len(random_data), expecting_data=random_data) self.assertRaises(FileNotFoundError, g.shm_unlink, name) # check that file was deleted diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index 780222761..9334ae8e6 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -204,7 +204,7 @@ class TestParser(BaseTest): k[p] = v.encode('ascii') for f in 'action transmission_type compressed'.split(): k.setdefault(f, b'\0') - for f in 'format more id data_sz width height x_offset y_offset data_height data_width num_cells num_lines z_index'.split(): + for f in 'format more id data_sz data_offset width height x_offset y_offset data_height data_width num_cells num_lines z_index'.split(): k.setdefault(f, 0) p = k.pop('payload', '').encode('utf-8') k['payload_sz'] = len(p)