Fix compiler warnings from OS X Lion: a missing #include and some
[sgt/utils] / xcopy / xcopy.c
CommitLineData
9acadc2b 1/*
2 * xcopy: quickly pipe text data into, or out of, the primary X
3 * selection
4 */
5
9acadc2b 6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <stdarg.h>
10#include <math.h>
11#include <errno.h>
12#include <assert.h>
464d0c29 13#include <ctype.h>
14
15#include <unistd.h>
9acadc2b 16
17#include <X11/X.h>
8fb6d89b 18#include <X11/Intrinsic.h>
9acadc2b 19#include <X11/Xlib.h>
20#include <X11/Xutil.h>
21#include <X11/Xatom.h>
22
23int init_X(void);
24void run_X(void);
25void done_X(void);
26void full_redraw(void);
27void do_paste(Window window, Atom property, int Delete);
28
29char *pname; /* program name */
30
31void error (char *fmt, ...);
32
33/* set from command-line parameters */
34char *display = NULL;
3ffcf184 35enum { STRING, CTEXT, UTF8, TARGETS, TIMESTAMP, CUSTOM } mode = STRING;
5e36d477 36int use_clipboard = False;
3ffcf184 37char *custom_seltype = NULL;
acc02b82 38int fork_when_writing = True;
7d4520e0 39int use_cutbuffers = True;
40int use_outgoing_incr = True;
41int sel_delta = 16384;
9acadc2b 42
43/* selection data */
44char *seltext;
45int sellen, selsize;
9acadc2b 46
8fb6d89b 47/* incremental transfers still pending when we return to the event loop */
48struct incr {
49 Window window;
50 Atom property;
51 Atom type;
52 int format;
53 unsigned char *data;
54 size_t size;
55} *incrs = NULL;
56int nincrs = 0, incrsize = 0;
57
9acadc2b 58/* functional parameters */
59int reading; /* read instead of writing? */
60int convert_to_ctext = True; /* Xmb convert to compound text? */
6aa7b28f 61int verbose;
9acadc2b 62
da0f8522 63const char usagemsg[] =
64 "usage: xcopy [ -r ] [ -u | -c ] [ -C ]\n"
7d4520e0 65 "modes: -r read X selection and print on stdout\n"
66 " no -r write to X selection from stdin\n"
67 " read: -t get the list of targets available to retrieve\n"
3ffcf184 68 " -T get the time stamp of the selection contents\n"
69 " -a atom get an arbitrary form of the selection data\n"
7d4520e0 70 " -v verbosely report progress in reading selection\n"
71 "write: -F remain in foreground until selection reclaimed\n"
72 " -I do not attempt incremental transfers (INCR)\n"
73 " both: -u work with UTF8_STRING type selections\n"
74 " -c work with COMPOUND_TEXT type selections\n"
75 " -C suppress automatic conversion to COMPOUND_TEXT\n"
76 " -b use the CLIPBOARD rather than the PRIMARY selection\n"
77 " -d size transfer data in chunks of at most <size> (default 16384)\n"
78 " -B do not use root window cut buffers\n"
c52f9fb9 79 " also: xcopy --version report version number\n"
80 " xcopy --help display this help text\n"
81 " xcopy --licence display the (MIT) licence text\n"
da0f8522 82 ;
83
84void usage(void) {
85 fputs(usagemsg, stdout);
86}
87
88const char licencemsg[] =
b3d07da3 89 "xcopy is copyright 2001-2004,2008 Simon Tatham.\n"
da0f8522 90 "\n"
91 "Permission is hereby granted, free of charge, to any person\n"
92 "obtaining a copy of this software and associated documentation files\n"
93 "(the \"Software\"), to deal in the Software without restriction,\n"
94 "including without limitation the rights to use, copy, modify, merge,\n"
95 "publish, distribute, sublicense, and/or sell copies of the Software,\n"
96 "and to permit persons to whom the Software is furnished to do so,\n"
97 "subject to the following conditions:\n"
98 "\n"
99 "The above copyright notice and this permission notice shall be\n"
100 "included in all copies or substantial portions of the Software.\n"
101 "\n"
102 "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n"
103 "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n"
104 "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n"
105 "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n"
106 "BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n"
107 "ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n"
108 "CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n"
109 "SOFTWARE.\n"
110 ;
111
112void licence(void) {
113 fputs(licencemsg, stdout);
114}
115
116void version(void) {
117#define SVN_REV "$Revision$"
118 char rev[sizeof(SVN_REV)];
119 char *p, *q;
120
121 strcpy(rev, SVN_REV);
122
123 for (p = rev; *p && *p != ':'; p++);
124 if (*p) {
125 p++;
126 while (*p && isspace(*p)) p++;
127 for (q = p; *q && *q != '$'; q++);
128 if (*q) *q = '\0';
129 printf("xcopy revision %s\n", p);
130 } else {
131 printf("xcopy: unknown version\n");
132 }
133}
134
9acadc2b 135int main(int ac, char **av) {
136 int n;
137 int eventloop;
138
139 pname = *av;
140
141 /* parse the command line arguments */
3ffcf184 142 while (--ac > 0) {
9acadc2b 143 char *p = *++av;
144
145 if (!strcmp(p, "-display") || !strcmp(p, "-disp")) {
146 if (!av[1])
147 error ("option `%s' expects a parameter", p);
148 display = *++av, --ac;
149 } else if (!strcmp(p, "-r")) {
150 reading = True;
151 } else if (!strcmp(p, "-u")) {
152 mode = UTF8;
153 } else if (!strcmp(p, "-c")) {
154 mode = CTEXT;
155 } else if (!strcmp(p, "-C")) {
156 convert_to_ctext = False;
5e36d477 157 } else if (!strcmp(p, "-b")) {
158 use_clipboard = True;
3ffcf184 159 } else if (!strcmp(p, "-t")) {
160 mode = TARGETS;
161 } else if (!strcmp(p, "-T")) {
162 mode = TIMESTAMP;
7d4520e0 163 } else if (!strcmp(p, "-I")) {
164 use_outgoing_incr = False;
165 } else if (!strcmp(p, "-B")) {
166 use_cutbuffers = False;
3ffcf184 167 } else if (!strcmp(p, "-a")) {
168 if (--ac > 0)
169 custom_seltype = *++av;
170 else
171 error ("expected an argument to `-a'");
172 mode = CUSTOM;
7d4520e0 173 } else if (!strcmp(p, "-d")) {
174 if (--ac > 0)
175 sel_delta = atoi(*++av);
176 else
177 error ("expected an argument to `-d'");
acc02b82 178 } else if (!strcmp(p, "-F")) {
179 fork_when_writing = False;
6aa7b28f 180 } else if (!strcmp(p, "-v")) {
181 verbose = True;
da0f8522 182 } else if (!strcmp(p, "--help")) {
183 usage();
184 return 0;
185 } else if (!strcmp(p, "--version")) {
186 version();
187 return 0;
188 } else if (!strcmp(p, "--licence") || !strcmp(p, "--license")) {
189 licence();
190 return 0;
9acadc2b 191 } else if (*p=='-') {
192 error ("unrecognised option `%s'", p);
193 } else {
194 error ("no parameters required");
195 }
196 }
197
198 if (!reading) {
3ffcf184 199 if (mode == TARGETS || mode == TIMESTAMP || mode == CUSTOM) {
200 error ("%s not supported in writing mode; use -r",
201 (mode == TARGETS ? "-t" :
202 mode == TIMESTAMP ? "-T" :
203 /* mode == CUSTOM ? */ "-a"));
204 }
205 }
206
207 if (!reading) {
7d4520e0 208 seltext = malloc(sel_delta);
9acadc2b 209 if (!seltext)
210 error ("out of memory");
7d4520e0 211 selsize = sel_delta;
9acadc2b 212 sellen = 0;
213 do {
214 n = fread(seltext+sellen, 1, selsize-sellen, stdin);
215 sellen += n;
216 if (sellen >= selsize) {
7d4520e0 217 seltext = realloc(seltext, selsize += sel_delta);
9acadc2b 218 if (!seltext)
219 error ("out of memory");
220 }
221 } while (n > 0);
222 if (sellen == selsize) {
7d4520e0 223 seltext = realloc(seltext, selsize += sel_delta);
9acadc2b 224 if (!seltext)
225 error ("out of memory");
226 }
227 seltext[sellen] = '\0';
228 }
229
230 eventloop = init_X();
acc02b82 231 if (!reading && fork_when_writing) {
9acadc2b 232 /*
233 * If we are writing the selection, we must go into the
234 * background now.
235 */
236 int pid = fork();
237 if (pid < 0) {
238 error("unable to fork: %s", strerror(errno));
239 } else if (pid > 0) {
240 /*
241 * we are the parent; just exit
242 */
243 return 0;
244 }
245 /*
246 * we are the child
247 */
248 close(0);
249 close(1);
250 close(2);
251 chdir("/");
252 }
253 if (eventloop)
254 run_X();
255 done_X();
256 return 0;
257}
258
259/* handle errors */
260
261void error (char *fmt, ...) {
262 va_list ap;
263 char errbuf[200];
264
265 done_X();
266 va_start (ap, fmt);
267 vsprintf (errbuf, fmt, ap);
268 va_end (ap);
269 fprintf (stderr, "%s: %s\n", pname, errbuf);
270 exit (1);
271}
272
273/* begin the X interface */
274
275char *lcasename = "xcopy";
276char *ucasename = "XCopy";
277
278Display *disp = NULL;
279Window ourwin = None;
acc02b82 280Atom compound_text_atom, targets_atom, timestamp_atom, atom_atom, integer_atom;
633c13c1 281Atom multiple_atom, atom_pair_atom, incr_atom;
9acadc2b 282int screen, wwidth, wheight;
283
284Atom strtype = XA_STRING;
5e36d477 285Atom sel_atom = XA_PRIMARY;
3ffcf184 286Atom expected_type = (Atom)None;
287int expected_format = 8;
9acadc2b 288
6aa7b28f 289static const char *translate_atom(Display *disp, Atom atom)
290{
291 if (atom == None)
292 return "None";
293 else
294 return XGetAtomName(disp, atom);
295}
296
9acadc2b 297/*
298 * Returns TRUE if we need to enter an event loop, FALSE otherwise.
299 */
300int init_X(void) {
301 Window root;
302 int x = 0, y = 0, width = 512, height = 128;
303 int i, got = 0;
304 XWMHints wm_hints;
305 XSizeHints size_hints;
306 XClassHint class_hints;
307 XTextProperty textprop;
308 XGCValues gcv;
309
310 /* open the X display */
311 disp = XOpenDisplay (display);
312 if (!disp)
313 error ("unable to open display");
314
3ffcf184 315 targets_atom = XInternAtom(disp, "TARGETS", False);
acc02b82 316 timestamp_atom = XInternAtom(disp, "TIMESTAMP", False);
317 atom_atom = XInternAtom(disp, "ATOM", False);
e1c1021f 318 atom_pair_atom = XInternAtom(disp, "ATOM_PAIR", False);
319 multiple_atom = XInternAtom(disp, "MULTIPLE", False);
acc02b82 320 integer_atom = XInternAtom(disp, "INTEGER", False);
633c13c1 321 incr_atom = XInternAtom(disp, "INCR", False);
9acadc2b 322 if (mode == UTF8) {
323 strtype = XInternAtom(disp, "UTF8_STRING", False);
9acadc2b 324 } else if (mode == CTEXT) {
325 strtype = XInternAtom(disp, "COMPOUND_TEXT", False);
3ffcf184 326 } else if (mode == TARGETS) {
327 strtype = targets_atom;
acc02b82 328 expected_type = atom_atom;
3ffcf184 329 expected_format = 32;
330 } else if (mode == TIMESTAMP) {
acc02b82 331 strtype = timestamp_atom;
332 expected_type = integer_atom;
3ffcf184 333 expected_format = 32;
334 } else if (mode == CUSTOM) {
acc02b82 335 strtype = XInternAtom(disp, custom_seltype, True);
336 if (!strtype)
337 error ("atom '%s' does not exist on the server", custom_seltype);
3ffcf184 338 expected_format = 0;
9acadc2b 339 }
5e36d477 340 if (use_clipboard) {
341 sel_atom = XInternAtom(disp, "CLIPBOARD", False);
5e36d477 342 }
9acadc2b 343
344 /* get the screen and root-window */
345 screen = DefaultScreen (disp);
346 root = RootWindow (disp, screen);
347
348 x = y = 0;
349 width = height = 10; /* doesn't really matter */
350
351 /* actually create the window */
352 ourwin = XCreateSimpleWindow (disp, root, x, y, width, height,0,
353 BlackPixel(disp, screen),
354 WhitePixel(disp, screen));
355
356 /* resource class name */
357 class_hints.res_name = lcasename;
358 class_hints.res_class = ucasename;
359 XSetClassHint (disp, ourwin, &class_hints);
360
361 /* do selection fiddling */
362 if (reading) {
363 /*
3a3007a4 364 * We are reading the selection. Call XConvertSelection to
365 * request transmission of the selection data in the
366 * appropriate format; the X event loop will then wait to
633c13c1 367 * receive the data from the selection owner. Also we need to
368 * make sure we receive PropertyNotify events, for INCR
369 * transfers.
3a3007a4 370 *
371 * If there is no selection owner, look in the cut buffer
372 * property on the root window.
9acadc2b 373 */
633c13c1 374 XSelectInput(disp, ourwin, PropertyChangeMask);
7d4520e0 375 if (XGetSelectionOwner(disp, sel_atom) != None) {
9acadc2b 376 Atom sel_property = XInternAtom(disp, "VT_SELECTION", False);
6aa7b28f 377 if (verbose)
378 fprintf(stderr, "calling XConvertSelection: selection=%s"
379 " target=%s property=%s requestor=%08lx\n",
380 translate_atom(disp, sel_atom),
381 translate_atom(disp, strtype),
382 translate_atom(disp, sel_property),
383 ourwin);
5e36d477 384 XConvertSelection(disp, sel_atom, strtype,
9acadc2b 385 sel_property, ourwin, CurrentTime);
386 return True;
7d4520e0 387 } else if (use_cutbuffers) {
388 /* No primary selection, so use the cut buffer. */
389 if (verbose)
390 fprintf(stderr, "no selection owner: trying cut buffer\n");
391 if (strtype == XA_STRING)
392 do_paste(DefaultRootWindow(disp), XA_CUT_BUFFER0, True);
393 return False;
394 } else {
395 /* Last fallback: do nothing. */
396 return False;
9acadc2b 397 }
398 } else {
399 /*
7d4520e0 400 * We are writing to the selection, so we establish ourselves
401 * as selection owner.
402 *
403 * Also place the data in CUT_BUFFER0, if it isn't of an
404 * exotic type (cut buffers can only take ordinary string
405 * data, it turns out) or bigger than our maximum chunk size.
9acadc2b 406 */
5e36d477 407 XSetSelectionOwner (disp, sel_atom, ourwin, CurrentTime);
408 if (XGetSelectionOwner (disp, sel_atom) != ourwin)
b3d07da3 409 error ("unable to obtain primary X selection");
9acadc2b 410 compound_text_atom = XInternAtom(disp, "COMPOUND_TEXT", False);
7d4520e0 411 if (strtype == XA_STRING && sellen <= sel_delta && use_cutbuffers) {
9acadc2b 412 /*
413 * ICCCM-required cut buffer initialisation.
414 */
464d0c29 415 static const unsigned char emptystring[] = {0};
9acadc2b 416 XChangeProperty(disp, root, XA_CUT_BUFFER0,
464d0c29 417 XA_STRING, 8, PropModeAppend, emptystring, 0);
9acadc2b 418 XChangeProperty(disp, root, XA_CUT_BUFFER1,
464d0c29 419 XA_STRING, 8, PropModeAppend, emptystring, 0);
9acadc2b 420 XChangeProperty(disp, root, XA_CUT_BUFFER2,
464d0c29 421 XA_STRING, 8, PropModeAppend, emptystring, 0);
9acadc2b 422 XChangeProperty(disp, root, XA_CUT_BUFFER3,
464d0c29 423 XA_STRING, 8, PropModeAppend, emptystring, 0);
9acadc2b 424 XChangeProperty(disp, root, XA_CUT_BUFFER4,
464d0c29 425 XA_STRING, 8, PropModeAppend, emptystring, 0);
9acadc2b 426 XChangeProperty(disp, root, XA_CUT_BUFFER5,
464d0c29 427 XA_STRING, 8, PropModeAppend, emptystring, 0);
9acadc2b 428 XChangeProperty(disp, root, XA_CUT_BUFFER6,
464d0c29 429 XA_STRING, 8, PropModeAppend, emptystring, 0);
9acadc2b 430 XChangeProperty(disp, root, XA_CUT_BUFFER7,
464d0c29 431 XA_STRING, 8, PropModeAppend, emptystring, 0);
9acadc2b 432 /*
433 * Rotate the cut buffers and add our text in CUT_BUFFER0.
434 */
435 XRotateBuffers(disp, 1);
436 XStoreBytes(disp, seltext, sellen);
437 }
438 return True;
439 }
440}
441
8fb6d89b 442void write_data(Window requestor, Atom property, Atom type, int format,
443 void *vdata, size_t size)
444{
445 int bformat = format / 8; /* bytes per element */
446 unsigned char *data = (unsigned char *)vdata;
447 XEvent ev;
448
7d4520e0 449 if (!use_outgoing_incr || size * bformat <= sel_delta) {
8fb6d89b 450 XChangeProperty(disp, requestor, property, type, format,
451 PropModeReplace, data, size);
452 } else {
453 /*
454 * For large data, an incremental transfer as per ICCCM 2.7.2.
455 */
456 Cardinal totalsize = size * bformat;
457 Cardinal sent, thissize;
458
459 /*
460 * We're going to need PropertyNotify events on the target
461 * window to tell us when to send the next chunk.
462 */
463 XSelectInput(disp, requestor, PropertyChangeMask);
464
465 /*
466 * Start by sending a single 32-bit word with type INCR giving
467 * the total size in bytes.
468 */
469 XChangeProperty(disp, requestor, property, incr_atom, 32,
470 PropModeReplace, (unsigned char *)&totalsize, 1);
471
472 /*
473 * Now set up an entry in our list of ongoing incremental
474 * transfers, so that whenever that property is deleted, we'll
475 * send the next batch.
476 */
477 if (nincrs >= incrsize) {
478 incrsize = nincrs * 9 / 8 + 16;
479 incrs = realloc(incrs, incrsize * sizeof(*incrs));
480 if (!incrs)
481 error ("out of memory");
482 }
483 incrs[nincrs].window = requestor;
484 incrs[nincrs].property = property;
485 incrs[nincrs].type = type;
486 incrs[nincrs].format = format;
487 incrs[nincrs].size = totalsize;
488 incrs[nincrs].data = malloc(totalsize);
489 if (!incrs[nincrs].data)
490 error("out of memory");
491 memcpy(incrs[nincrs].data, data, size);
492 nincrs++;
493 }
494}
495
e1c1021f 496Atom convert_sel_inner(Window requestor, Atom target, Atom property) {
497 if (target == strtype) {
8fb6d89b 498 write_data(requestor, property, strtype, 8, seltext, sellen);
e1c1021f 499 return property;
500 } else if (target == compound_text_atom && convert_to_ctext) {
501 XTextProperty tp;
8fb6d89b 502 XmbTextListToTextProperty(disp, &seltext, 1, XCompoundTextStyle, &tp);
503 write_data(requestor, property, target, tp.format, tp.value,tp.nitems);
e1c1021f 504 return property;
505 } else if (target == targets_atom) {
506 Atom targets[16];
507 int len = 0;
508 targets[len++] = timestamp_atom;
509 targets[len++] = targets_atom;
510 targets[len++] = multiple_atom;
511 targets[len++] = strtype;
512 if (strtype != compound_text_atom && convert_to_ctext)
513 targets[len++] = compound_text_atom;
8fb6d89b 514 write_data(requestor, property, atom_atom, 32, targets, len);
e1c1021f 515 return property;
516 } else if (target == timestamp_atom) {
517 Time rettime = CurrentTime;
8fb6d89b 518 write_data(requestor, property, integer_atom, 32, &rettime, 1);
e1c1021f 519 return property;
520 } else {
521 return None;
522 }
523}
524
525Atom convert_sel_outer(Window requestor, Atom target, Atom property) {
461ec27a 526 /*
527 * ICCCM 2.2 says that obsolete clients requesting the selection
528 * request may not specify a property name under which they want
529 * the data written to their window; selection owners are
530 * encouraged to support such clients by reusing the selection
531 * target name as the property.
532 */
533 if (property == None)
534 property = target;
535
e1c1021f 536 if (target == multiple_atom) {
537 /*
538 * Support for the MULTIPLE selection type, since it's
539 * specified as required in the ICCCM. Completely
540 * untested, though, because I have no idea of what X
541 * application might implement it for me to test against.
542 */
543
7d4520e0 544 int size = sel_delta;
e1c1021f 545 Atom actual_type;
546 int actual_format, i;
464d0c29 547 unsigned long nitems, bytes_after, nread;
e1c1021f 548 unsigned char *data;
549 Atom *adata;
550
b06414b8 551 if (property == (Atom)None)
552 return None; /* ICCCM says this isn't allowed */
553
e1c1021f 554 /*
555 * Fetch the requestor's window property giving a list of
556 * selection requests.
557 */
558 while (XGetWindowProperty(disp, requestor, property, 0, size,
559 False, AnyPropertyType, &actual_type,
560 &actual_format, &nitems, &bytes_after,
561 (unsigned char **) &data) == Success &&
562 nitems * (actual_format / 8) == size) {
563 XFree(data);
564 size *= 3 / 2;
565 }
566
567 if (actual_type != atom_pair_atom || actual_format != 32) {
568 XFree(data);
569 return None;
570 }
571
572 adata = (Atom *)data;
573
464d0c29 574 for (i = 0; i+1 < (long)nitems; i += 2) {
b06414b8 575 if (adata[i+1] != (Atom)None) /* ICCCM says this isn't allowed */
576 adata[i+1] = convert_sel_inner(requestor, adata[i],
577 adata[i+1]);
e1c1021f 578 }
579
580 XChangeProperty (disp, requestor, property,
581 atom_pair_atom, 32, PropModeReplace,
582 data, nitems);
583
584 XFree(data);
585
586 return property;
587 } else {
b06414b8 588 if (property == (Atom)None)
589 property = target; /* ICCCM says this is a sensible default */
e1c1021f 590 return convert_sel_inner(requestor, target, property);
591 }
592}
593
9acadc2b 594void run_X(void) {
595 XEvent ev, e2;
8fb6d89b 596 int i, j;
9acadc2b 597
598 while (1) {
599 XNextEvent (disp, &ev);
600 if (reading) {
601 switch (ev.type) {
602 case SelectionNotify:
6aa7b28f 603 if (verbose)
604 fprintf(stderr, "got SelectionNotify: requestor=%08lx "
605 "selection=%s target=%s property=%s\n",
606 ev.xselection.requestor,
607 translate_atom(disp, ev.xselection.selection),
608 translate_atom(disp, ev.xselection.target),
609 translate_atom(disp, ev.xselection.property));
610
9acadc2b 611 if (ev.xselection.property != None)
612 do_paste(ev.xselection.requestor,
633c13c1 613 ev.xselection.property, False);
9acadc2b 614 return;
615 }
616 } else {
4bf3e4f3 617 int have_ownership = True;
f8381827 618
9acadc2b 619 switch (ev.type) {
620 case SelectionClear:
621 /* Selection has been cleared by another app. */
4bf3e4f3 622 have_ownership = False;
f8381827 623 break;
9acadc2b 624 case SelectionRequest:
4bf3e4f3 625 if (have_ownership) {
626 e2.xselection.type = SelectionNotify;
627 e2.xselection.requestor = ev.xselectionrequest.requestor;
628 e2.xselection.selection = ev.xselectionrequest.selection;
629 e2.xselection.target = ev.xselectionrequest.target;
630 e2.xselection.time = ev.xselectionrequest.time;
631 e2.xselection.property =
632 convert_sel_outer(ev.xselectionrequest.requestor,
633 ev.xselectionrequest.target,
634 ev.xselectionrequest.property);
635 XSendEvent (disp, ev.xselectionrequest.requestor,
636 False, 0, &e2);
637 }
8fb6d89b 638 break;
639 case PropertyNotify:
640 for (i = j = 0; i < nincrs; i++) {
641 int keep = True;
642 if (incrs[i].window == ev.xproperty.window &&
643 incrs[i].property == ev.xproperty.atom &&
644 ev.xproperty.state == PropertyDelete) {
645 size_t thissize = incrs[i].size;
7d4520e0 646 if (thissize > sel_delta)
647 thissize = sel_delta;
8fb6d89b 648
649 XChangeProperty(disp,
650 incrs[i].window, incrs[i].property,
651 incrs[i].type, incrs[i].format,
652 PropModeReplace, incrs[i].data,
653 thissize / (incrs[i].format/8));
654
655 if (thissize == 0) {
656 /*
657 * If we've just sent a zero-length block,
658 * the incremental transfer is over and we
659 * should delete this entry.
660 */
661 keep = False;
662 }
663
664 incrs[i].data += thissize;
665 incrs[i].size -= thissize;
666 }
667 if (keep) {
668 if (j != i)
669 incrs[j] = incrs[i];
670 j++;
671 }
672 }
673 nincrs = j;
674 break;
9acadc2b 675 }
4bf3e4f3 676 if (nincrs == 0 && !have_ownership)
f8381827 677 return;
9acadc2b 678 }
679 }
680}
681
682void done_X(void) {
683 int i;
684
685 if (ourwin != None)
686 XDestroyWindow (disp, ourwin);
687 if (disp)
688 XCloseDisplay (disp);
689}
690
633c13c1 691void do_paste(Window window, Atom property, int cutbuffer) {
9acadc2b 692 Atom actual_type;
693 int actual_format, i;
464d0c29 694 unsigned long nitems, bytes_after, nread;
9acadc2b 695 unsigned char *data;
633c13c1 696 int incremental = False;
697 XEvent ev;
9acadc2b 698
699 nread = 0;
7d4520e0 700 while (XGetWindowProperty(disp, window, property, nread / 4, sel_delta / 4,
633c13c1 701 !cutbuffer, AnyPropertyType, &actual_type,
9acadc2b 702 &actual_format, &nitems, &bytes_after,
703 (unsigned char **) &data) == Success) {
6aa7b28f 704 if (verbose)
705 fprintf(stderr, "got %ld items of %d-byte data, type=%s;"
706 " %ld to go\n", nitems, actual_format,
707 translate_atom(disp, actual_type), bytes_after);
708
633c13c1 709 /*
710 * ICCCM 2.7.2: if we receive data with the type atom set to
711 * INCR, it indicates that the actual data will arrive in
712 * multiple chunks, terminating with a zero-length one.
713 * Between each pair, we must wait for a PropertyNotify event
714 * which tells us that the next chunk has arrived.
715 */
716 if (actual_type == incr_atom && !cutbuffer) {
717 incremental = True;
718 /*
719 * Immediately wait for the first chunk of real data.
720 */
721 do {
722 XMaskEvent(disp, PropertyChangeMask, &ev);
723 } while (ev.xproperty.state != PropertyNewValue);
724 /*
725 * And loop straight back round to read it.
726 */
727 continue;
728 }
729
3ffcf184 730 if (nitems > 0) {
5340832d 731 /*
732 * We expect all returned chunks of data to be
733 * multiples of 4 bytes (because we can only request
734 * the subsequent starting offset in 4-byte
735 * increments). Of course you can store an odd number
736 * of bytes in a selection, so this can't be the case
737 * every time XGetWindowProperty returns; but it
738 * should be the case every time it returns _and there
739 * is more data to come_.
740 *
741 * Hence, whenever XGetWindowProperty returns, we
742 * verify that the size of the data returned _last_
743 * time was divisible by 4.
744 */
745 if ((nread & 3) != 0) {
746 error("unexpected data size: %d read (not a multiple"
b3d07da3 747 " of 4), but more to come", nread);
5340832d 748 }
749
3ffcf184 750 if (expected_type != (Atom)None && actual_type != expected_type) {
6aa7b28f 751 const char *expout = translate_atom(disp, expected_type);
752 const char *gotout = translate_atom(disp, actual_type);
b3d07da3 753 error("unexpected data type: expected %s, got %s",
3ffcf184 754 expout, gotout);
755 }
756 if (expected_format && expected_format != actual_format) {
b3d07da3 757 error("unexpected data format: expected %d-bit, got %d-bit",
3ffcf184 758 expected_format, actual_format);
759 }
760 }
761
762 if (nitems > 0) {
763 if (mode == TARGETS) {
764 assert(actual_format == 32);
765 int i;
766 for (i = 0; i < nitems; i++) {
767 Atom x = ((Atom *)data)[i];
6aa7b28f 768 printf("%s\n", translate_atom(disp, x));
3ffcf184 769 }
770 } else if (mode == TIMESTAMP) {
771 assert(actual_format == 32);
772 Time x = ((Time *)data)[0];
773 printf("%lu\n", (unsigned long)x);
774 } else {
775 fwrite(data, actual_format / 8, nitems, stdout);
776 nread += nitems * actual_format / 8;
777 }
9acadc2b 778 }
779 XFree(data);
05ecc810 780 if (bytes_after == 0) {
633c13c1 781 /*
782 * We've come to the end of the property we're reading.
783 */
784 if (incremental) {
785 /*
786 * For an incremental transfer, this means we wait for
787 * another property to be dumped in the same place on
788 * our window, and loop round again reading that. The
789 * exception is if the total length of the property we
790 * got was zero, which signals the end.
791 */
792 if (nread == 0 && nitems == 0)
793 break; /* all done */
794
795 /* otherwise wait for the next chunk */
796 do {
797 XMaskEvent(disp, PropertyChangeMask, &ev);
798 } while (ev.xproperty.state != PropertyNewValue);
799
800 /* which we read from the beginning */
801 nread = 0;
802 } else {
05ecc810 803 break;
633c13c1 804 }
05ecc810 805 }
9acadc2b 806 }
807}