Skip to content

Commit 019158c

Browse files
committed
Merge branch 'copilot/update-dnd-protocol-symlink-handling' of https://github.com/kovidgoyal/kitty
2 parents a92b381 + 4d2b63f commit 019158c

4 files changed

Lines changed: 331 additions & 47 deletions

File tree

docs/dnd-protocol.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,8 @@ encoded and might be chunked if the directory has a lot of entries.
179179

180180
``idx`` is an arbitrary 32 bit integer that acts as a handle to this
181181
directory. The client can now read the files in this directory using requests of the form
182-
``t=d:x=idx:y=num:r=request_id``, here ``num`` is the 0-based index into the list of
183-
directory entries previously transmitted to the client, where, ``0`` will
182+
``t=d:x=idx:y=num:r=request_id``, here ``num`` is the 1-based index into the list of
183+
directory entries previously transmitted to the client, where, ``1`` will
184184
correspond to the first entry in the directory. Once the client is done
185185
reading a directory it should transmit ``t=d:x=idx:r=request_id`` to the terminal. The
186186
terminal can then free any resources associated with that directory. The

gen/apc_parsers.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,15 @@ def generate(
114114
payload_is_base64: bool = True,
115115
start_parsing_at: int = 1,
116116
field_sep: str = ',',
117+
post_init: str = '',
117118
) -> str:
118119
type_map = resolve_keys(keymap)
119120
keys_enum = enum(keymap)
120121
handle_key = parse_key(keymap)
121122
flag_keys = parse_flag(keymap, type_map, command_class)
122123
int_keys, uint_keys = parse_number(keymap)
123124
report_cmd = cmd_for_report(report_name, keymap, type_map, payload_allowed, payload_is_base64)
125+
post_init_line = f'\n {post_init}' if post_init else ''
124126
extra_init = ''
125127
if payload_allowed:
126128
payload_after_value = "case ';': state = PAYLOAD; break;"
@@ -163,7 +165,7 @@ def generate(
163165
{extra_init}
164166
enum PARSER_STATES {{ KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE {payload} }};
165167
enum PARSER_STATES state = KEY, value_state = FLAG;
166-
{command_class} g = {{0}};
168+
{command_class} g = {{0}};{post_init_line}
167169
unsigned int i, code;
168170
uint64_t lcode; int64_t accumulator;
169171
bool is_negative; (void)is_negative;

kitty/dnd.c

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -710,9 +710,6 @@ drop_find_dir_handle(Window *w, uint32_t id) {
710710
* send the listing to the client as a t=d:x=handle_id response. */
711711
static void
712712
drop_send_dir_listing(Window *w, const char *path) {
713-
struct stat st;
714-
if (stat(path, &st) < 0) { drop_send_error(w, EIO); return; }
715-
716713
DIR *dir = opendir(path);
717714
if (!dir) {
718715
switch (errno) {
@@ -723,17 +720,11 @@ drop_send_dir_listing(Window *w, const char *path) {
723720
return;
724721
}
725722

726-
/* Build null-separated payload: unique_id\0entry1\0entry2\0... */
723+
/* Build null-separated payload: entry1\0entry2\0... */
727724
size_t payload_cap = 4096, payload_sz = 0;
728725
char *payload = malloc(payload_cap);
729726
if (!payload) { closedir(dir); drop_send_error(w, EIO); return; }
730727

731-
/* First entry: unique identifier (device:inode) */
732-
char uid[64];
733-
int uid_len = snprintf(uid, sizeof(uid), "%llu:%llu",
734-
(unsigned long long)st.st_dev,
735-
(unsigned long long)st.st_ino);
736-
737728
#define APPEND(s, n) do { \
738729
size_t _n = (size_t)(n); \
739730
size_t _need = payload_sz + _n + 1; \
@@ -748,8 +739,6 @@ drop_send_dir_listing(Window *w, const char *path) {
748739
payload[payload_sz++] = 0; \
749740
} while(0)
750741

751-
APPEND(uid, uid_len);
752-
753742
/* Collect directory entries */
754743
size_t ents_cap = 16, ents_num = 0;
755744
char **ents = malloc(sizeof(char *) * ents_cap);
@@ -909,8 +898,8 @@ do_drop_handle_dir_request(Window *w, uint32_t handle_id, int32_t entry_num) {
909898
drop_send_error(w, EIO); return true;
910899
}
911900

912-
struct stat st;
913-
if (stat(full, &st) < 0) {
901+
struct stat lst;
902+
if (lstat(full, &lst) < 0) {
914903
switch (errno) {
915904
case ENOENT: case ENOTDIR: case ELOOP: drop_send_error(w, ENOENT); break;
916905
case EACCES: case EPERM: drop_send_error(w, EPERM); break;
@@ -919,10 +908,32 @@ do_drop_handle_dir_request(Window *w, uint32_t handle_id, int32_t entry_num) {
919908
return true;
920909
}
921910

922-
if (S_ISDIR(st.st_mode)) {
911+
if (S_ISLNK(lst.st_mode)) {
912+
/* Symlink: send the symlink target as t=r:X=1 */
913+
char target[PATH_MAX];
914+
ssize_t tlen = readlink(full, target, sizeof(target) - 1);
915+
if (tlen < 0) {
916+
switch (errno) {
917+
case ENOENT: case ENOTDIR: drop_send_error(w, ENOENT); break;
918+
case EACCES: case EPERM: drop_send_error(w, EPERM); break;
919+
default: drop_send_error(w, EIO); break;
920+
}
921+
return true;
922+
}
923+
target[tlen] = '\0';
924+
char hdr[128];
925+
int hdr_sz = snprintf(hdr, sizeof(hdr), "\x1b]%d;t=r:X=1", DND_CODE);
926+
if (w->drop.current_request_id)
927+
hdr_sz += snprintf(hdr + hdr_sz, sizeof(hdr) - hdr_sz, ":r=%u", (unsigned)w->drop.current_request_id);
928+
queue_payload_to_child(w->id, w->drop.client_id, &w->drop.pending, hdr, hdr_sz, target, (size_t)tlen, true);
929+
queue_payload_to_child(w->id, w->drop.client_id, &w->drop.pending, hdr, hdr_sz, NULL, 0, true);
930+
return true;
931+
}
932+
933+
if (S_ISDIR(lst.st_mode)) {
923934
drop_send_dir_listing(w, full);
924935
return true;
925-
} else if (S_ISREG(st.st_mode)) {
936+
} else if (S_ISREG(lst.st_mode)) {
926937
return drop_send_file_data(w, full);
927938
} else {
928939
drop_send_error(w, EINVAL);

0 commit comments

Comments
 (0)