xsel.c (66495B)
1/* 2 * xsel -- manipulate the X selection 3 * Copyright (C) 2001 Conrad Parker <conrad@vergenet.net> 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and 6 * its documentation for any purpose is hereby granted without fee, provided 7 * that the above copyright notice appear in all copies and that both that 8 * copyright notice and this permission notice appear in supporting 9 * documentation. No representations are made about the suitability of this 10 * software for any purpose. It is provided "as is" without express or 11 * implied warranty. 12 */ 13 14#ifdef HAVE_CONFIG_H 15#include "config.h" 16#endif 17 18#include <stdio.h> 19#include <stdlib.h> 20#include <stdarg.h> 21#include <errno.h> 22#include <unistd.h> 23#include <string.h> 24#include <pwd.h> 25#include <sys/types.h> 26#include <sys/stat.h> 27#include <sys/time.h> 28#include <fcntl.h> 29#include <sys/time.h> 30#include <setjmp.h> 31#include <signal.h> 32#include <X11/Xlib.h> 33#include <X11/Xatom.h> 34#include <X11/Xutil.h> 35 36#include "xsel.h" 37 38 39/* The name we were invoked as (argv[0]) */ 40static char * progname; 41 42/* Verbosity level for debugging */ 43static int debug_level = DEBUG_LEVEL; 44 45/* Our X Display and Window */ 46static Display * display; 47static Window window; 48 49/* Maxmimum request size supported by this X server */ 50static long max_req; 51 52/* Our timestamp for all operations */ 53static Time timestamp; 54 55static Atom timestamp_atom; /* The TIMESTAMP atom */ 56static Atom multiple_atom; /* The MULTIPLE atom */ 57static Atom targets_atom; /* The TARGETS atom */ 58static Atom delete_atom; /* The DELETE atom */ 59static Atom incr_atom; /* The INCR atom */ 60static Atom null_atom; /* The NULL atom */ 61static Atom text_atom; /* The TEXT atom */ 62static Atom utf8_atom; /* The UTF8 atom */ 63static Atom compound_text_atom; /* The COMPOUND_TEXT atom */ 64 65/* Number of selection targets served by this. 66 * (MULTIPLE, INCR, TARGETS, TIMESTAMP, DELETE, TEXT, UTF8_STRING and STRING) 67 * NB. We do not currently serve COMPOUND_TEXT; we can retrieve it but do not 68 * perform charset conversion. 69 */ 70#define MAX_NUM_TARGETS 9 71static int NUM_TARGETS; 72static Atom supported_targets[MAX_NUM_TARGETS]; 73 74/* do_zeroflush: Use only last zero-separated part of input. 75 * All previous parts are discarded */ 76static Bool do_zeroflush = False; 77 78/* do_follow: Follow mode for output */ 79static Bool do_follow = False; 80 81/* nodaemon: Disable daemon mode if True. */ 82static Bool no_daemon = False; 83 84/* logfile: name of file to log error messages to when detached */ 85static char logfile[MAXFNAME]; 86 87/* fstat() on stdin and stdout */ 88static struct stat in_statbuf, out_statbuf; 89 90static int total_input = 0; 91static int current_alloc = 0; 92 93static long timeout = 0; 94static struct itimerval timer; 95static struct itimerval zerot; 96 97#define USEC_PER_SEC 1000000 98 99static int saved_argc; 100static char ** saved_argv; 101 102/* 103 * usage () 104 * 105 * print usage information. 106 */ 107static void 108usage (void) 109{ 110 printf ("Usage: xsel [options]\n"); 111 printf ("Manipulate the X selection.\n\n"); 112 printf ("By default the current selection is output and not modified if both\n"); 113 printf ("standard input and standard output are terminals (ttys). Otherwise,\n"); 114 printf ("the current selection is output if standard output is not a terminal\n"); 115 printf ("(tty), and the selection is set from standard input if standard input\n"); 116 printf ("is not a terminal (tty). If any input or output options are given then\n"); 117 printf ("the program behaves only in the requested mode.\n\n"); 118 printf ("If both input and output is required then the previous selection is\n"); 119 printf ("output before being replaced by the contents of standard input.\n\n"); 120 printf ("Input options\n"); 121 printf (" -a, --append Append standard input to the selection\n"); 122 printf (" -f, --follow Append to selection as standard input grows\n"); 123 printf (" -z, --zeroflush Overwrites selection when zero ('\\0') is received\n"); 124 printf (" -i, --input Read standard input into the selection\n\n"); 125 printf ("Output options\n"); 126 printf (" -o, --output Write the selection to standard output\n\n"); 127 printf ("Action options\n"); 128 printf (" -c, --clear Clear the selection\n"); 129 printf (" -d, --delete Request that the selection be cleared and that\n"); 130 printf (" the application owning it delete its contents\n\n"); 131 printf ("Selection options\n"); 132 printf (" -p, --primary Operate on the PRIMARY selection (default)\n"); 133 printf (" -s, --secondary Operate on the SECONDARY selection\n"); 134 printf (" -b, --clipboard Operate on the CLIPBOARD selection\n\n"); 135 printf (" -k, --keep Do not modify the selections, but make the PRIMARY\n"); 136 printf (" and SECONDARY selections persist even after the\n"); 137 printf (" programs they were selected in exit.\n"); 138 printf (" -x, --exchange Exchange the PRIMARY and SECONDARY selections\n\n"); 139 printf ("X options\n"); 140 printf (" --display displayname\n"); 141 printf (" Specify the connection to the X server\n"); 142 printf (" -m wm, --name wm Name with the process will be identified\n"); 143 printf (" -t ms, --selectionTimeout ms\n"); 144 printf (" Specify the timeout in milliseconds within which the\n"); 145 printf (" selection must be retrieved. A value of 0 (zero)\n"); 146 printf (" specifies no timeout (default)\n\n"); 147 printf ("Miscellaneous options\n"); 148 printf (" --trim Remove newline ('\\n') char from end of input / output\n"); 149 printf (" -l, --logfile Specify file to log errors to when detached.\n"); 150 printf (" -n, --nodetach Do not detach from the controlling terminal. Without\n"); 151 printf (" this option, xsel will fork to become a background\n"); 152 printf (" process in input, exchange and keep modes.\n\n"); 153 printf (" -h, --help Display this help and exit\n"); 154 printf (" -v, --verbose Print informative messages\n"); 155 printf (" --version Output version information and exit\n\n"); 156 printf ("Please report bugs to <conrad@vergenet.net>.\n"); 157} 158 159/* 160 * exit_err (fmt) 161 * 162 * Print a formatted error message and errno information to stderr, 163 * then exit with return code 1. 164 */ 165static void 166exit_err (const char * fmt, ...) 167{ 168 va_list ap; 169 int errno_save; 170 char buf[MAXLINE]; 171 int n; 172 173 errno_save = errno; 174 175 va_start (ap, fmt); 176 177 snprintf (buf, MAXLINE, "%s: ", progname); 178 n = strlen (buf); 179 180 vsnprintf (buf+n, MAXLINE-n, fmt, ap); 181 n = strlen (buf); 182 183 snprintf (buf+n, MAXLINE-n, ": %s\n", strerror (errno_save)); 184 185 fflush (stdout); /* in case stdout and stderr are the same */ 186 fputs (buf, stderr); 187 fflush (NULL); 188 189 va_end (ap); 190 exit (1); 191} 192 193/* 194 * print_err (fmt) 195 * 196 * Print a formatted error message to stderr. 197 */ 198static void 199print_err (const char * fmt, ...) 200{ 201 va_list ap; 202 int errno_save; 203 char buf[MAXLINE]; 204 int n; 205 206 errno_save = errno; 207 208 va_start (ap, fmt); 209 210 snprintf (buf, MAXLINE, "%s: ", progname); 211 n = strlen (buf); 212 213 vsnprintf (buf+n, MAXLINE-n, fmt, ap); 214 n = strlen (buf); 215 216 fflush (stdout); /* in case stdout and stderr are the same */ 217 fputs (buf, stderr); 218 fputc ('\n', stderr); 219 fflush (NULL); 220 221 va_end (ap); 222} 223 224/* 225 * print_debug (level, fmt) 226 * 227 * Print a formatted debugging message of level 'level' to stderr 228 */ 229#define print_debug(x,y...) {if (x <= debug_level) print_err (y);} 230 231/* 232 * get_atom_name (atom) 233 * 234 * Returns a string with a printable name for the Atom 'atom'. 235 */ 236static char * 237get_atom_name (Atom atom) 238{ 239 char * ret; 240 static char atom_name[MAXLINE+2]; /* unused extra char to avoid 241 string-op-truncation warning */ 242 243 if (atom == None) return "None"; 244 if (atom == XA_STRING) return "STRING"; 245 if (atom == XA_PRIMARY) return "PRIMARY"; 246 if (atom == XA_SECONDARY) return "SECONDARY"; 247 if (atom == timestamp_atom) return "TIMESTAMP"; 248 if (atom == multiple_atom) return "MULTIPLE"; 249 if (atom == targets_atom) return "TARGETS"; 250 if (atom == delete_atom) return "DELETE"; 251 if (atom == incr_atom) return "INCR"; 252 if (atom == null_atom) return "NULL"; 253 if (atom == text_atom) return "TEXT"; 254 if (atom == utf8_atom) return "UTF8_STRING"; 255 256 ret = XGetAtomName (display, atom); 257 strncpy (atom_name, ret, MAXLINE+1); 258 if (atom_name[MAXLINE] != '\0') 259 { 260 atom_name[MAXLINE-3] = '.'; 261 atom_name[MAXLINE-2] = '.'; 262 atom_name[MAXLINE-1] = '.'; 263 atom_name[MAXLINE] = '\0'; 264 } 265 XFree (ret); 266 267 return atom_name; 268} 269 270/* 271 * debug_property (level, requestor, property, target, length) 272 * 273 * Print debugging information (at level 'level') about a property received. 274 */ 275static void 276debug_property (int level, Window requestor, Atom property, Atom target, 277 unsigned long length) 278{ 279 print_debug (level, "Got window property: requestor 0x%x, property 0x%x, target 0x%x %s, length %ld bytes", requestor, property, target, get_atom_name (target), length); 280} 281 282/* 283 * xs_malloc (size) 284 * 285 * Malloc wrapper. Always returns a successful allocation. Exits if the 286 * allocation didn't succeed. 287 */ 288static void * 289xs_malloc (size_t size) 290{ 291 void * ret; 292 293 if (size == 0) size = 1; 294 if ((ret = malloc (size)) == NULL) { 295 exit_err ("malloc error"); 296 } 297 298 return ret; 299} 300 301/* 302 * xs_strdup (s) 303 * 304 * strdup wrapper for unsigned char * 305 */ 306#define xs_strdup(s) ((unsigned char *) _xs_strdup ((const char *)s)) 307static char * _xs_strdup (const char * s) 308{ 309 char * ret; 310 311 if (s == NULL) return NULL; 312 if ((ret = strdup(s)) == NULL) { 313 exit_err ("strdup error"); 314 } 315 316 return ret; 317} 318 319/* 320 * xs_strlen (s) 321 * 322 * strlen wrapper for unsigned char * 323 */ 324#define xs_strlen(s) (strlen ((const char *) s)) 325 326/* 327 * xs_strncpy (s) 328 * 329 * strncpy wrapper for unsigned char * 330 */ 331#define xs_strncpy(dest,s,n) (_xs_strncpy ((char *)dest, (const char *)s, n)) 332static char * 333_xs_strncpy (char * dest, const char * src, size_t n) 334{ 335 if (n > 0) { 336 strncpy (dest, src, n-1); 337 dest[n-1] = '\0'; 338 } 339 return dest; 340} 341 342/* 343 * get_xdg_cache_home () 344 * 345 * Get the user's cache directory 346 */ 347static char * 348get_xdg_cache_home (void) 349{ 350 char * cachedir; 351 char * homedir; 352 static const char * slashbasename = "/.cache"; 353 354 if ((cachedir = getenv ("XDG_CACHE_HOME")) == NULL) { 355 if ((homedir = getenv ("HOME")) == NULL) { 356 exit_err ("no HOME directory"); 357 } 358 cachedir = xs_malloc (strlen (homedir) + strlen (slashbasename) + 1); 359 strcpy (cachedir, homedir); 360 strcat (cachedir, slashbasename); 361 } else { 362 cachedir = _xs_strdup (cachedir); 363 } 364 365 mkdir (cachedir, S_IRWXU|S_IRGRP|S_IXGRP); 366 367 return cachedir; 368} 369 370/* 371 * The set of terminal signals we block while handling SelectionRequests. 372 * 373 * If we exit in the middle of handling a SelectionRequest, we might leave the 374 * requesting client hanging, so we try to be nice and finish handling 375 * requests before terminating. Hence we block SIG{ALRM,INT,TERM} while 376 * handling requests and unblock them only while waiting in XNextEvent(). 377 */ 378static sigset_t exit_sigs; 379 380static void block_exit_sigs(void) 381{ 382 sigprocmask (SIG_BLOCK, &exit_sigs, NULL); 383} 384 385static void unblock_exit_sigs(void) 386{ 387 sigprocmask (SIG_UNBLOCK, &exit_sigs, NULL); 388} 389 390/* The jmp_buf to longjmp out of the signal handler */ 391static sigjmp_buf env_alrm; 392 393/* 394 * alarm_handler (sig) 395 * 396 * Signal handler for catching SIGALRM. 397 */ 398static void 399alarm_handler (int sig) 400{ 401 siglongjmp (env_alrm, 1); 402} 403 404/* 405 * set_timer_timeout () 406 * 407 * Set timer parameters according to specified timeout. 408 */ 409static void 410set_timer_timeout (void) 411{ 412 timer.it_interval.tv_sec = timeout / USEC_PER_SEC; 413 timer.it_interval.tv_usec = timeout % USEC_PER_SEC; 414 timer.it_value.tv_sec = timeout / USEC_PER_SEC; 415 timer.it_value.tv_usec = timeout % USEC_PER_SEC; 416} 417 418/* 419 * set_daemon_timeout () 420 * 421 * Set up a timer to cause the daemon to exit after the desired 422 * amount of time. 423 */ 424static void 425set_daemon_timeout (void) 426{ 427 if (signal (SIGALRM, alarm_handler) == SIG_ERR) { 428 exit_err ("error setting timeout handler"); 429 } 430 431 set_timer_timeout (); 432 433 if (sigsetjmp (env_alrm, 0) == 0) { 434 setitimer (ITIMER_REAL, &timer, (struct itimerval *)0); 435 } else { 436 print_debug (D_INFO, "daemon exiting after %d ms", timeout / 1000); 437 exit (0); 438 } 439} 440 441 442/* 443 * become_daemon () 444 * 445 * Perform the required procedure to become a daemon process, as 446 * outlined in the Unix programming FAQ: 447 * http://www.steve.org.uk/Reference/Unix/faq_2.html#SEC16 448 * and open a logfile. 449 */ 450static void 451become_daemon (void) 452{ 453 pid_t pid; 454 int null_r_fd, null_w_fd, log_fd; 455 char * cachedir; 456 457 if (no_daemon) { 458 /* If the user has specified a timeout, enforce it even if we don't 459 * actually daemonize */ 460 set_daemon_timeout (); 461 return; 462 } 463 464 cachedir = get_xdg_cache_home(); 465 466 /* Check that we can open a logfile before continuing */ 467 468 /* If the user has specified a --logfile, use that ... */ 469 if (logfile[0] == '\0') { 470 /* ... otherwise use the default logfile */ 471 snprintf (logfile, MAXFNAME, "%s/xsel.log", cachedir); 472 } 473 474 /* Make sure to create the logfile with sane permissions */ 475 log_fd = open (logfile, O_WRONLY|O_APPEND|O_CREAT, 0600); 476 if (log_fd == -1) { 477 exit_err ("error opening logfile %s for writing", logfile); 478 } 479 print_debug (D_INFO, "opened logfile %s", logfile); 480 481 if ((pid = fork()) == -1) { 482 exit_err ("error forking"); 483 } else if (pid > 0) { 484 _exit (0); 485 } 486 487 if (setsid () == -1) { 488 exit_err ("setsid error"); 489 } 490 491 if ((pid = fork()) == -1) { 492 exit_err ("error forking"); 493 } else if (pid > 0) { 494 _exit (0); 495 } 496 497 umask (0); 498 499 if (chdir (cachedir) == -1) { 500 print_debug (D_WARN, "Could not chdir to %s\n", cachedir); 501 if (chdir ("/") == -1) { 502 exit_err ("Error chdir to /"); 503 } 504 } 505 506 /* dup2 /dev/null on stdin unless following input */ 507 if (!do_follow) { 508 null_r_fd = open ("/dev/null", O_RDONLY); 509 if (null_r_fd == -1) { 510 exit_err ("error opening /dev/null for reading"); 511 } 512 if (dup2 (null_r_fd, 0) == -1) { 513 exit_err ("error duplicating /dev/null on stdin"); 514 } 515 } 516 517 /* dup2 /dev/null on stdout */ 518 null_w_fd = open ("/dev/null", O_WRONLY|O_APPEND); 519 if (null_w_fd == -1) { 520 exit_err ("error opening /dev/null for writing"); 521 } 522 if (dup2 (null_w_fd, 1) == -1) { 523 exit_err ("error duplicating /dev/null on stdout"); 524 } 525 526 /* dup2 logfile on stderr */ 527 if (dup2 (log_fd, 2) == -1) { 528 exit_err ("error duplicating logfile %s on stderr", logfile); 529 } 530 531 set_daemon_timeout (); 532 533 free (cachedir); 534} 535 536/* 537 * get_timestamp () 538 * 539 * Get the current X server time. 540 * 541 * This is done by doing a zero-length append to a random property of the 542 * window, and checking the time on the subsequent PropertyNotify event. 543 * 544 * PRECONDITION: the window must have PropertyChangeMask set. 545 */ 546static Time 547get_timestamp (void) 548{ 549 XEvent event; 550 551 XChangeProperty (display, window, XA_WM_NAME, XA_STRING, 8, 552 PropModeAppend, NULL, 0); 553 554 while (1) { 555 XNextEvent (display, &event); 556 557 if (event.type == PropertyNotify) 558 return event.xproperty.time; 559 } 560} 561 562/* 563 * SELECTION RETRIEVAL 564 * =================== 565 * 566 * The following functions implement retrieval of an X selection, 567 * optionally within a user-specified timeout. 568 * 569 * 570 * Selection timeout handling. 571 * --------------------------- 572 * 573 * The selection retrieval can time out if no response is received within 574 * a user-specified time limit. In order to ensure we time the entire 575 * selection retrieval, we use an interval timer and catch SIGALRM. 576 * [Calling select() on the XConnectionNumber would only provide a timeout 577 * to the first XEvent.] 578 */ 579 580/* 581 * get_append_property () 582 * 583 * Get a window property and append its data to a buffer at a given offset 584 * pointed to by *offset. 'offset' is modified by this routine to point to 585 * the end of the data. 586 * 587 * Returns True if more data is available for receipt. 588 * 589 * If an error is encountered, the buffer is free'd. 590 */ 591static Bool 592get_append_property (XSelectionEvent * xsl, unsigned char ** buffer, 593 unsigned long * offset, unsigned long * alloc) 594{ 595 unsigned char * ptr; 596 Atom target; 597 int format; 598 unsigned long bytesafter, length; 599 unsigned char * value; 600 601 XGetWindowProperty (xsl->display, xsl->requestor, xsl->property, 602 0L, 1000000, True, (Atom)AnyPropertyType, 603 &target, &format, &length, &bytesafter, &value); 604 605 debug_property (D_TRACE, xsl->requestor, xsl->property, target, length); 606 607 if (target != XA_STRING && target != utf8_atom && 608 target != compound_text_atom) { 609 print_debug (D_OBSC, "target %s not XA_STRING nor UTF8_STRING in get_append_property()", 610 get_atom_name (target)); 611 free (*buffer); 612 *buffer = NULL; 613 return False; 614 } else if (length == 0) { 615 /* A length of 0 indicates the end of the transfer */ 616 print_debug (D_TRACE, "Got zero length property; end of INCR transfer"); 617 return False; 618 } else if (format == 8) { 619 if (*offset + length + 1 > *alloc) { 620 *alloc = *offset + length + 1; 621 if ((*buffer = realloc (*buffer, *alloc)) == NULL) { 622 exit_err ("realloc error"); 623 } 624 } 625 ptr = *buffer + *offset; 626 memcpy (ptr, value, length); 627 ptr[length] = '\0'; 628 *offset += length; 629 print_debug (D_TRACE, "Appended %d bytes to buffer\n", length); 630 } else { 631 print_debug (D_WARN, "Retrieved non-8-bit data\n"); 632 } 633 634 return True; 635} 636 637 638/* 639 * wait_incr_selection (selection) 640 * 641 * Retrieve a property of target type INCR. Perform incremental retrieval 642 * and return the resulting data. 643 */ 644static unsigned char * 645wait_incr_selection (Atom selection, XSelectionEvent * xsl, int init_alloc) 646{ 647 XEvent event; 648 unsigned char * incr_base = NULL, * incr_ptr = NULL; 649 unsigned long incr_alloc = 0, incr_xfer = 0; 650 Bool wait_prop = True; 651 652 print_debug (D_TRACE, "Initialising incremental retrieval of at least %d bytes\n", init_alloc); 653 654 /* Take an interest in the requestor */ 655 XSelectInput (xsl->display, xsl->requestor, PropertyChangeMask); 656 657 incr_alloc = init_alloc; 658 incr_base = xs_malloc (incr_alloc); 659 incr_ptr = incr_base; 660 661 print_debug (D_TRACE, "Deleting property that informed of INCR transfer"); 662 XDeleteProperty (xsl->display, xsl->requestor, xsl->property); 663 664 print_debug (D_TRACE, "Waiting on PropertyNotify events"); 665 while (wait_prop) { 666 XNextEvent (xsl->display, &event); 667 668 switch (event.type) { 669 case PropertyNotify: 670 if (event.xproperty.state != PropertyNewValue) break; 671 672 wait_prop = get_append_property (xsl, &incr_base, &incr_xfer, 673 &incr_alloc); 674 break; 675 default: 676 break; 677 } 678 } 679 680 /* when zero length found, finish up & delete last */ 681 XDeleteProperty (xsl->display, xsl->requestor, xsl->property); 682 683 print_debug (D_TRACE, "Finished INCR retrieval"); 684 685 return incr_base; 686} 687 688/* 689 * wait_selection (selection, request_target) 690 * 691 * Block until we receive a SelectionNotify event, and return its 692 * contents; or NULL in the case of a deletion or error. This assumes we 693 * have already called XConvertSelection, requesting a string (explicitly 694 * XA_STRING) or deletion (delete_atom). 695 */ 696static unsigned char * 697wait_selection (Atom selection, Atom request_target) 698{ 699 XEvent event; 700 Atom target; 701 int format; 702 unsigned long bytesafter, length; 703 unsigned char * value, * retval = NULL; 704 Bool keep_waiting = True; 705 706 while (keep_waiting) { 707 XNextEvent (display, &event); 708 709 switch (event.type) { 710 case SelectionNotify: 711 if (event.xselection.selection != selection) break; 712 713 if (event.xselection.property == None) { 714 print_debug (D_WARN, "Conversion refused"); 715 value = NULL; 716 keep_waiting = False; 717 } else if (event.xselection.property == null_atom && 718 request_target == delete_atom) { 719 } else { 720 XGetWindowProperty (event.xselection.display, 721 event.xselection.requestor, 722 event.xselection.property, 0L, 1000000, 723 False, (Atom)AnyPropertyType, &target, 724 &format, &length, &bytesafter, &value); 725 726 debug_property (D_TRACE, event.xselection.requestor, 727 event.xselection.property, target, length); 728 729 if (request_target == delete_atom && value == NULL) { 730 keep_waiting = False; 731 } else if (target == incr_atom) { 732 /* Handle INCR transfers */ 733 retval = wait_incr_selection (selection, &event.xselection, 734 *(long *)value); 735 keep_waiting = False; 736 } else if (target != utf8_atom && target != XA_STRING && 737 target != compound_text_atom && 738 request_target != delete_atom) { 739 /* Report non-TEXT atoms */ 740 print_debug (D_WARN, "Selection (type %s) is not a string.", 741 get_atom_name (target)); 742 free (retval); 743 retval = NULL; 744 keep_waiting = False; 745 } else { 746 retval = xs_strdup (value); 747 XFree (value); 748 keep_waiting = False; 749 } 750 751 XDeleteProperty (event.xselection.display, 752 event.xselection.requestor, 753 event.xselection.property); 754 755 } 756 break; 757 default: 758 break; 759 } 760 } 761 762 /* Now that we've received the SelectionNotify event, clear any 763 * remaining timeout. */ 764 if (timeout > 0) { 765 // setitimer (ITIMER_REAL, (struct itimerval *)0, (struct itimerval *)0); 766 setitimer (ITIMER_REAL, &zerot, (struct itimerval *)0); 767 } 768 769 return retval; 770} 771 772/* 773 * get_selection (selection, request_target) 774 * 775 * Retrieves the specified selection and returns its value. 776 * 777 * If a non-zero timeout is specified then set a virtual interval 778 * timer. Return NULL and print an error message if the timeout 779 * expires before the selection has been retrieved. 780 */ 781static unsigned char * 782get_selection (Atom selection, Atom request_target) 783{ 784 Atom prop; 785 unsigned char * retval; 786 787 prop = XInternAtom (display, "XSEL_DATA", False); 788 XConvertSelection (display, selection, request_target, prop, window, 789 timestamp); 790 XSync (display, False); 791 792 if (timeout > 0) { 793 if (signal (SIGALRM, alarm_handler) == SIG_ERR) { 794 exit_err ("error setting timeout handler"); 795 } 796 797 set_timer_timeout (); 798 799 if (sigsetjmp (env_alrm, 0) == 0) { 800 setitimer (ITIMER_REAL, &timer, (struct itimerval *)0); 801 retval = wait_selection (selection, request_target); 802 } else { 803 print_debug (D_WARN, "selection timed out"); 804 retval = NULL; 805 } 806 } else { 807 retval = wait_selection (selection, request_target); 808 } 809 810 return retval; 811} 812 813/* 814 * get_selection_text (Atom selection) 815 * 816 * Retrieve a text selection. First attempt to retrieve it as UTF_STRING, 817 * and if that fails attempt to retrieve it as a plain XA_STRING. 818 * 819 * NB. Before implementing this, an attempt was made to query TARGETS and 820 * request UTF8_STRING only if listed there, as described in: 821 * http://www.pps.jussieu.fr/~jch/software/UTF8_STRING/UTF8_STRING.text 822 * However, that did not seem to work reliably when tested against various 823 * applications (eg. Mozilla Firefox). This method is of course more 824 * reliable. 825 */ 826static unsigned char * 827get_selection_text (Atom selection) 828{ 829 unsigned char * retval; 830 831 if ((retval = get_selection (selection, utf8_atom)) == NULL) 832 retval = get_selection (selection, XA_STRING); 833 834 return retval; 835} 836 837 838/* 839 * SELECTION SETTING 840 * ================= 841 * 842 * The following functions allow a given selection to be set, appended to 843 * or cleared, or to exchange the primary and secondary selections. 844 */ 845 846/* 847 * copy_sel (s) 848 * 849 * Copy a string into a new selection buffer, and intitialise 850 * current_alloc and total_input to exactly its length. 851 */ 852static unsigned char * 853copy_sel (unsigned char * s) 854{ 855 if (s) { 856 current_alloc = total_input = xs_strlen (s); 857 return xs_strdup (s); 858 } 859 current_alloc = total_input = 0; 860 return NULL; 861} 862 863/* 864 * read_input (read_buffer, do_select) 865 * 866 * Read input from stdin into the specified read_buffer. 867 * 868 * read_buffer must have been dynamically allocated before calling this 869 * function, or be NULL. Input is read until end-of-file is reached, and 870 * read_buffer will be reallocated to accomodate the entire contents of 871 * the input. read_buffer, which may have been reallocated, is returned 872 * upon completion. 873 * 874 * If 'do_select' is True, this function will first check if any data 875 * is available for reading, and return immediately if not. 876 */ 877static unsigned char * 878read_input (unsigned char * read_buffer, Bool do_select) 879{ 880 int insize = in_statbuf.st_blksize; 881 unsigned char * new_buffer = NULL; 882 int d, fatal = 0, nfd; 883 ssize_t n; 884 fd_set fds; 885 struct timeval select_timeout; 886 887 do { 888 889 if (do_select) { 890try_read: 891 /* Check if data is available for reading -- if not, return immediately */ 892 FD_ZERO (&fds); 893 FD_SET (0, &fds); 894 895 select_timeout.tv_sec = (time_t)0; 896 select_timeout.tv_usec = (time_t)0; 897 898 nfd = select (1, &fds, NULL, NULL, &select_timeout); 899 if (nfd == -1) { 900 if (errno == EINTR) goto try_read; 901 else exit_err ("select error"); 902 } else if (nfd == 0) { 903 print_debug (D_TRACE, "No data available for reading"); 904 break; 905 } 906 } 907 908 /* check if buffer is full */ 909 if (current_alloc == total_input) { 910 if ((d = (current_alloc % insize)) != 0) current_alloc += (insize-d); 911 current_alloc *= 2; 912 new_buffer = realloc (read_buffer, current_alloc); 913 if (new_buffer == NULL) { 914 exit_err ("realloc error"); 915 } 916 read_buffer = new_buffer; 917 } 918 919 /* read the remaining data, up to the optimal block length */ 920 n = read (0, &read_buffer[total_input], 921 MIN(current_alloc - total_input, insize)); 922 if (n == -1) { 923 switch (errno) { 924 case EAGAIN: 925 case EINTR: 926 break; 927 default: 928 perror ("read error"); 929 fatal = 1; 930 break; 931 } 932 } 933 total_input += n; 934 } while (n != 0 && !fatal); 935 936 read_buffer[total_input] = '\0'; 937 938 if(do_zeroflush && total_input > 0) { 939 int i; 940 for(i=total_input-1; i>=0; i--) { 941 if(read_buffer[i] == '\0') { 942 print_debug (D_TRACE, "Flushing input at %d", i); 943 memmove(&read_buffer[0], &read_buffer[i+1], total_input - i); 944 total_input = total_input - i - 1; 945 read_buffer[total_input] = '\0'; 946 break; 947 } 948 } 949 } 950 951 print_debug (D_TRACE, "Accumulated %d bytes input", total_input); 952 953 return read_buffer; 954} 955 956/* 957 * initialise_read (read_buffer) 958 * 959 * Initialises the read_buffer and the state variable current_alloc. 960 * read_buffer is reallocated to accomodate either the entire input 961 * if stdin is a regular file, or at least one block of input otherwise. 962 * If the supplied read_buffer is NULL, a new buffer will be allocated. 963 */ 964static unsigned char * 965initialise_read (unsigned char * read_buffer) 966{ 967 int insize = in_statbuf.st_blksize; 968 unsigned char * new_buffer = NULL; 969 970 if (S_ISREG (in_statbuf.st_mode) && in_statbuf.st_size > 0) { 971 current_alloc += in_statbuf.st_size; 972 } else { 973 current_alloc += insize; 974 } 975 976 if ((new_buffer = realloc (read_buffer, current_alloc)) == NULL) { 977 exit_err ("realloc error"); 978 } 979 980 read_buffer = new_buffer; 981 982 return read_buffer; 983} 984 985/* Forward declaration of refuse_all_incr () */ 986static void 987refuse_all_incr (void); 988 989/* 990 * handle_x_errors () 991 * 992 * XError handler. 993 */ 994static int 995handle_x_errors (Display * display, XErrorEvent * eev) 996{ 997 char err_buf[MAXLINE]; 998 999 /* Make sure to send a refusal to all waiting INCR requests 1000 * and delete the corresponding properties. */ 1001 if (eev->error_code == BadAlloc) refuse_all_incr (); 1002 1003 XGetErrorText (display, eev->error_code, err_buf, MAXLINE); 1004 exit_err (err_buf); 1005 1006 return 0; 1007} 1008 1009/* 1010 * clear_selection (selection) 1011 * 1012 * Clears the specified X selection 'selection'. This requests that no 1013 * process should own 'selection'; thus the X server will respond to 1014 * SelectionRequests with an empty property and we don't need to leave 1015 * a daemon hanging around to service this selection. 1016 */ 1017static void 1018clear_selection (Atom selection) 1019{ 1020 XSetSelectionOwner (display, selection, None, timestamp); 1021 /* Call XSync to ensure this operation completes before program 1022 * termination, especially if this is all we are doing. */ 1023 XSync (display, False); 1024} 1025 1026/* 1027 * own_selection (selection) 1028 * 1029 * Requests ownership of the X selection. Returns True if ownership was 1030 * granted, and False otherwise. 1031 */ 1032static Bool 1033own_selection (Atom selection) 1034{ 1035 Window owner; 1036 1037 XSetSelectionOwner (display, selection, window, timestamp); 1038 /* XGetSelectionOwner does a round trip to the X server, so there is 1039 * no need to call XSync here. */ 1040 owner = XGetSelectionOwner (display, selection); 1041 if (owner != window) { 1042 return False; 1043 } else { 1044 XSetErrorHandler (handle_x_errors); 1045 return True; 1046 } 1047} 1048 1049 1050static IncrTrack * incrtrack_list = NULL; 1051 1052/* 1053 * add_incrtrack (it) 1054 * 1055 * Add 'it' to the head of incrtrack_list. 1056 */ 1057static void 1058add_incrtrack (IncrTrack * it) 1059{ 1060 if (incrtrack_list) { 1061 incrtrack_list->prev = it; 1062 } 1063 it->prev = NULL; 1064 it->next = incrtrack_list; 1065 incrtrack_list = it; 1066} 1067 1068/* 1069 * remove_incrtrack (it) 1070 * 1071 * Remove 'it' from incrtrack_list. 1072 */ 1073static void 1074remove_incrtrack (IncrTrack * it) 1075{ 1076 if (it->prev) { 1077 it->prev->next = it->next; 1078 } 1079 if (it->next) { 1080 it->next->prev = it->prev; 1081 } 1082 1083 if (incrtrack_list == it) { 1084 incrtrack_list = it->next; 1085 } 1086} 1087 1088/* 1089 * fresh_incrtrack () 1090 * 1091 * Create a new incrtrack, and add it to incrtrack_list. 1092 */ 1093static IncrTrack * 1094fresh_incrtrack (void) 1095{ 1096 IncrTrack * it; 1097 1098 it = xs_malloc (sizeof (IncrTrack)); 1099 add_incrtrack (it); 1100 1101 return it; 1102} 1103 1104/* 1105 * trash_incrtrack (it) 1106 * 1107 * Remove 'it' from incrtrack_list, and free it. 1108 */ 1109static void 1110trash_incrtrack (IncrTrack * it) 1111{ 1112 remove_incrtrack (it); 1113 free (it); 1114} 1115 1116/* 1117 * find_incrtrack (atom) 1118 * 1119 * Find the IncrTrack structure within incrtrack_list pertaining to 'atom', 1120 * if it exists. 1121 */ 1122static IncrTrack * 1123find_incrtrack (Atom atom) 1124{ 1125 IncrTrack * iti; 1126 1127 for (iti = incrtrack_list; iti; iti = iti->next) { 1128 if (atom == iti->property) return iti; 1129 } 1130 1131 return NULL; 1132} 1133 1134/* Forward declaration of handle_multiple() */ 1135static HandleResult 1136handle_multiple (Display * display, Window requestor, Atom property, 1137 unsigned char * sel, Atom selection, Time time, 1138 MultTrack * mparent); 1139 1140/* Forward declaration of process_multiple() */ 1141static HandleResult 1142process_multiple (MultTrack * mt, Bool do_parent); 1143 1144/* 1145 * confirm_incr (it) 1146 * 1147 * Confirm the selection request of ITER tracked by 'it'. 1148 */ 1149static void 1150notify_incr (IncrTrack * it, HandleResult hr) 1151{ 1152 XSelectionEvent ev; 1153 1154 /* Call XSync here to make sure any BadAlloc errors are caught before 1155 * confirming the conversion. */ 1156 XSync (it->display, False); 1157 1158 print_debug (D_TRACE, "Confirming conversion"); 1159 1160 /* Prepare a SelectionNotify event to send, placing the selection in the 1161 * requested property. */ 1162 ev.type = SelectionNotify; 1163 ev.display = it->display; 1164 ev.requestor = it->requestor; 1165 ev.selection = it->selection; 1166 ev.time = it->time; 1167 ev.target = it->target; 1168 1169 if (hr & HANDLE_ERR) ev.property = None; 1170 else ev.property = it->property; 1171 1172 XSendEvent (display, ev.requestor, False, 1173 (unsigned long)NULL, (XEvent *)&ev); 1174} 1175 1176/* 1177 * refuse_all_incr () 1178 * 1179 * Refuse all INCR transfers in progress. ASSUMES that this is called in 1180 * response to an error, and that the program is about to bail out; 1181 * ie. incr_track is not cleaned out. 1182 */ 1183static void 1184refuse_all_incr (void) 1185{ 1186 IncrTrack * it; 1187 1188 for (it = incrtrack_list; it; it = it->next) { 1189 XDeleteProperty (it->display, it->requestor, it->property); 1190 notify_incr (it, HANDLE_ERR); 1191 /* Don't bother trashing and list-removing these; we are about to 1192 * bail out anyway. */ 1193 } 1194} 1195 1196/* 1197 * complete_incr (it) 1198 * 1199 * Finish off an INCR retrieval. If it was part of a multiple, continue 1200 * that; otherwise, send confirmation that this completed. 1201 */ 1202static void 1203complete_incr (IncrTrack * it, HandleResult hr) 1204{ 1205 MultTrack * mparent = it->mparent; 1206 1207 if (mparent) { 1208 trash_incrtrack (it); 1209 process_multiple (mparent, True); 1210 } else { 1211 notify_incr (it, hr); 1212 trash_incrtrack (it); 1213 } 1214} 1215 1216/* 1217 * notify_multiple (mt, hr) 1218 * 1219 * Confirm the selection request initiated with MULTIPLE tracked by 'mt'. 1220 */ 1221static void 1222notify_multiple (MultTrack * mt, HandleResult hr) 1223{ 1224 XSelectionEvent ev; 1225 1226 /* Call XSync here to make sure any BadAlloc errors are caught before 1227 * confirming the conversion. */ 1228 XSync (mt->display, False); 1229 1230 /* Prepare a SelectionNotify event to send, placing the selection in the 1231 * requested property. */ 1232 ev.type = SelectionNotify; 1233 ev.display = mt->display; 1234 ev.requestor = mt->requestor; 1235 ev.selection = mt->selection; 1236 ev.time = mt->time; 1237 ev.target = multiple_atom; 1238 1239 if (hr & HANDLE_ERR) ev.property = None; 1240 else ev.property = mt->property; 1241 1242 XSendEvent (display, ev.requestor, False, 1243 (unsigned long)NULL, (XEvent *)&ev); 1244} 1245 1246/* 1247 * complete_multiple (mt, do_parent, hr) 1248 * 1249 * Complete a MULTIPLE transfer. Iterate to its parent MULTIPLE if 1250 * 'do_parent' is true. If there is not parent MULTIPLE, send notification 1251 * of its completion with status 'hr'. 1252 */ 1253static void 1254complete_multiple (MultTrack * mt, Bool do_parent, HandleResult hr) 1255{ 1256 MultTrack * mparent = mt->mparent; 1257 1258 if (mparent) { 1259 free (mt); 1260 if (do_parent) process_multiple (mparent, True); 1261 } else { 1262 notify_multiple (mt, hr); 1263 free (mt); 1264 } 1265} 1266 1267/* 1268 * change_property (display, requestor, property, target, format, mode, 1269 * data, nelements) 1270 * 1271 * Wrapper to XChangeProperty that performs INCR transfer if required and 1272 * returns status of entire transfer. 1273 */ 1274static HandleResult 1275change_property (Display * display, Window requestor, Atom property, 1276 Atom target, int format, int mode, 1277 unsigned char * data, int nelements, 1278 Atom selection, Time time, MultTrack * mparent) 1279{ 1280 XSelectionEvent ev; 1281 long nr_bytes; 1282 IncrTrack * it; 1283 1284 print_debug (D_TRACE, "change_property ()"); 1285 1286 nr_bytes = nelements * format / 8; 1287 1288 if (nr_bytes <= max_req) { 1289 print_debug (D_TRACE, "data within maximum request size"); 1290 XChangeProperty (display, requestor, property, target, format, mode, 1291 data, nelements); 1292 1293 return HANDLE_OK; 1294 } 1295 1296 /* else */ 1297 print_debug (D_TRACE, "large data transfer"); 1298 1299 1300 /* Send a SelectionNotify event */ 1301 ev.type = SelectionNotify; 1302 ev.display = display; 1303 ev.requestor = requestor; 1304 ev.selection = selection; 1305 ev.time = time; 1306 ev.target = target; 1307 ev.property = property; 1308 1309 XSelectInput (ev.display, ev.requestor, PropertyChangeMask); 1310 1311 XChangeProperty (ev.display, ev.requestor, ev.property, incr_atom, 32, 1312 PropModeReplace, (unsigned char *)&nr_bytes, 1); 1313 1314 XSendEvent (display, requestor, False, 1315 (unsigned long)NULL, (XEvent *)&ev); 1316 1317 /* Set up the IncrTrack to track this */ 1318 it = fresh_incrtrack (); 1319 1320 it->mparent = mparent; 1321 it->state = S_INCR_1; 1322 it->display = display; 1323 it->requestor = requestor; 1324 it->property = property; 1325 it->selection = selection; 1326 it->time = time; 1327 it->target = target; 1328 it->format = format; 1329 it->data = data; 1330 it->nelements = nelements; 1331 it->offset = 0; 1332 1333 /* Maximum nr. of elements that can be transferred in one go */ 1334 it->max_elements = max_req * 8 / format; 1335 1336 /* Nr. of elements to transfer in this instance */ 1337 it->chunk = MIN (it->max_elements, it->nelements - it->offset); 1338 1339 /* Wait for that property to get deleted */ 1340 print_debug (D_TRACE, "Waiting on initial property deletion (%s)", 1341 get_atom_name (it->property)); 1342 1343 return HANDLE_INCOMPLETE; 1344} 1345 1346static HandleResult 1347incr_stage_1 (IncrTrack * it) 1348{ 1349 /* First pass: PropModeReplace, from data, size chunk */ 1350 print_debug (D_TRACE, "Writing first chunk (%d bytes) (target 0x%x %s) to property 0x%x of requestor 0x%x", it->chunk, it->target, get_atom_name(it->target), it->property, it->requestor); 1351 XChangeProperty (it->display, it->requestor, it->property, it->target, 1352 it->format, PropModeReplace, it->data, it->chunk); 1353 1354 it->offset += it->chunk; 1355 1356 /* wait for PropertyNotify events */ 1357 print_debug (D_TRACE, "Waiting on subsequent deletions ..."); 1358 1359 it->state = S_INCR_2; 1360 1361 return HANDLE_INCOMPLETE; 1362} 1363 1364static HandleResult 1365incr_stage_2 (IncrTrack * it) 1366{ 1367 it->chunk = MIN (it->max_elements, it->nelements - it->offset); 1368 1369 if (it->chunk <= 0) { 1370 1371 /* Now write zero-length data to the property */ 1372 XChangeProperty (it->display, it->requestor, it->property, it->target, 1373 it->format, PropModeAppend, NULL, 0); 1374 it->state = S_NULL; 1375 print_debug (D_TRACE, "Set si to state S_NULL"); 1376 return HANDLE_OK; 1377 } else { 1378 print_debug (D_TRACE, "Writing chunk (%d bytes) to property", 1379 it->chunk); 1380 XChangeProperty (it->display, it->requestor, it->property, it->target, 1381 it->format, PropModeAppend, it->data+it->offset, 1382 it->chunk); 1383 it->offset += it->chunk; 1384 print_debug (D_TRACE, "%d bytes remaining", 1385 it->nelements - it->offset); 1386 return HANDLE_INCOMPLETE; 1387 } 1388} 1389 1390 1391/* 1392 * handle_timestamp (display, requestor, property) 1393 * 1394 * Handle a TIMESTAMP request. 1395 */ 1396static HandleResult 1397handle_timestamp (Display * display, Window requestor, Atom property, 1398 Atom selection, Time time, MultTrack * mparent) 1399{ 1400 return 1401 change_property (display, requestor, property, XA_INTEGER, 32, 1402 PropModeReplace, (unsigned char *)×tamp, 1, 1403 selection, time, mparent); 1404} 1405 1406/* 1407 * handle_targets (display, requestor, property) 1408 * 1409 * Handle a TARGETS request. 1410 */ 1411static HandleResult 1412handle_targets (Display * display, Window requestor, Atom property, 1413 Atom selection, Time time, MultTrack * mparent) 1414{ 1415 Atom * targets_cpy; 1416 HandleResult r; 1417 1418 targets_cpy = malloc (sizeof (supported_targets)); 1419 memcpy (targets_cpy, supported_targets, sizeof (supported_targets)); 1420 1421 r = change_property (display, requestor, property, XA_ATOM, 32, 1422 PropModeReplace, (unsigned char *)targets_cpy, 1423 NUM_TARGETS, selection, time, mparent); 1424 free(targets_cpy); 1425 return r; 1426} 1427 1428/* 1429 * handle_string (display, requestor, property, sel) 1430 * 1431 * Handle a STRING request; setting 'sel' as the data 1432 */ 1433static HandleResult 1434handle_string (Display * display, Window requestor, Atom property, 1435 unsigned char * sel, Atom selection, Time time, 1436 MultTrack * mparent) 1437{ 1438 return 1439 change_property (display, requestor, property, XA_STRING, 8, 1440 PropModeReplace, sel, xs_strlen(sel), 1441 selection, time, mparent); 1442} 1443 1444/* 1445 * handle_utf8_string (display, requestor, property, sel) 1446 * 1447 * Handle a UTF8_STRING request; setting 'sel' as the data 1448 */ 1449static HandleResult 1450handle_utf8_string (Display * display, Window requestor, Atom property, 1451 unsigned char * sel, Atom selection, Time time, 1452 MultTrack * mparent) 1453{ 1454 return 1455 change_property (display, requestor, property, utf8_atom, 8, 1456 PropModeReplace, sel, xs_strlen(sel), 1457 selection, time, mparent); 1458} 1459 1460/* 1461 * handle_delete (display, requestor, property) 1462 * 1463 * Handle a DELETE request. 1464 */ 1465static HandleResult 1466handle_delete (Display * display, Window requestor, Atom property) 1467{ 1468 XChangeProperty (display, requestor, property, null_atom, 0, 1469 PropModeReplace, NULL, 0); 1470 1471 return DID_DELETE; 1472} 1473 1474/* 1475 * process_multiple (mt, do_parent) 1476 * 1477 * Iterate through a MultTrack until it completes, or until one of its 1478 * entries initiates an interated selection. 1479 * 1480 * If 'do_parent' is true, and the actions proscribed in 'mt' are 1481 * completed during the course of this call, then process_multiple 1482 * is iteratively called on mt->mparent. 1483 */ 1484static HandleResult 1485process_multiple (MultTrack * mt, Bool do_parent) 1486{ 1487 HandleResult retval = HANDLE_OK; 1488 unsigned long i; 1489 1490 if (!mt) return retval; 1491 1492 for (; mt->index < mt->length; mt->index += 2) { 1493 i = mt->index; 1494 if (mt->atoms[i] == timestamp_atom) { 1495 retval |= handle_timestamp (mt->display, mt->requestor, mt->atoms[i+1], 1496 mt->selection, mt->time, mt); 1497 } else if (mt->atoms[i] == targets_atom) { 1498 retval |= handle_targets (mt->display, mt->requestor, mt->atoms[i+1], 1499 mt->selection, mt->time, mt); 1500 } else if (mt->atoms[i] == multiple_atom) { 1501 retval |= handle_multiple (mt->display, mt->requestor, mt->atoms[i+1], 1502 mt->sel, mt->selection, mt->time, mt); 1503 } else if (mt->atoms[i] == XA_STRING || mt->atoms[i] == text_atom) { 1504 retval |= handle_string (mt->display, mt->requestor, mt->atoms[i+1], 1505 mt->sel, mt->selection, mt->time, mt); 1506 } else if (mt->atoms[i] == utf8_atom) { 1507 retval |= handle_utf8_string (mt->display, mt->requestor, mt->atoms[i+1], 1508 mt->sel, mt->selection, mt->time, mt); 1509 } else if (mt->atoms[i] == delete_atom) { 1510 retval |= handle_delete (mt->display, mt->requestor, mt->atoms[i+1]); 1511 } else if (mt->atoms[i] == None) { 1512 /* the only other thing we know to handle is None, for which we 1513 * do nothing. This block is, like, __so__ redundant. Welcome to 1514 * Over-engineering 101 :) This comment is just here to keep the 1515 * logic documented and separate from the 'else' block. */ 1516 } else { 1517 /* for anything we don't know how to handle, we fail the conversion 1518 * by setting this: */ 1519 mt->atoms[i] = None; 1520 } 1521 1522 /* If any of the conversions failed, signify this by setting that 1523 * atom to None ...*/ 1524 if (retval & HANDLE_ERR) { 1525 mt->atoms[i] = None; 1526 } 1527 /* ... but don't propogate HANDLE_ERR */ 1528 retval &= (~HANDLE_ERR); 1529 1530 if (retval & HANDLE_INCOMPLETE) break; 1531 } 1532 1533 if ((retval & HANDLE_INCOMPLETE) == 0) { 1534 complete_multiple (mt, do_parent, retval); 1535 } 1536 1537 return retval; 1538} 1539 1540/* 1541 * continue_incr (it) 1542 * 1543 * Continue an incremental transfer of IncrTrack * it. 1544 * 1545 * NB. If the incremental transfer was part of a multiple request, this 1546 * function calls process_multiple with do_parent=True because it is 1547 * assumed we are continuing an interrupted ITER, thus we must continue 1548 * the multiple as its original handler did not complete. 1549 */ 1550static HandleResult 1551continue_incr (IncrTrack * it) 1552{ 1553 HandleResult retval = HANDLE_OK; 1554 1555 if (it->state == S_INCR_1) { 1556 retval = incr_stage_1 (it); 1557 } else if (it->state == S_INCR_2) { 1558 retval = incr_stage_2 (it); 1559 } 1560 1561 /* If that completed the INCR, deal with completion */ 1562 if ((retval & HANDLE_INCOMPLETE) == 0) { 1563 complete_incr (it, retval); 1564 } 1565 1566 return retval; 1567} 1568 1569/* 1570 * handle_multiple (display, requestor, property, sel, selection, time) 1571 * 1572 * Handle a MULTIPLE request; possibly setting 'sel' if any STRING 1573 * requests are processed within it. Return value has DID_DELETE bit set 1574 * if any delete requests are processed. 1575 * 1576 * NB. This calls process_multiple with do_parent=False because it is 1577 * assumed we are "handling" the multiple request on behalf of a 1578 * multiple already in progress, or (more likely) directly off a 1579 * SelectionRequest event. 1580 */ 1581static HandleResult 1582handle_multiple (Display * display, Window requestor, Atom property, 1583 unsigned char * sel, Atom selection, Time time, 1584 MultTrack * mparent) 1585{ 1586 MultTrack * mt; 1587 int format; 1588 Atom type; 1589 unsigned long bytesafter; 1590 HandleResult retval = HANDLE_OK; 1591 1592 mt = xs_malloc (sizeof (MultTrack)); 1593 1594 XGetWindowProperty (display, requestor, property, 0L, 1000000, 1595 False, (Atom)AnyPropertyType, &type, 1596 &format, &mt->length, &bytesafter, 1597 (unsigned char **)&mt->atoms); 1598 1599 /* Make sure we got the Atom list we want */ 1600 if (format != 32) return HANDLE_OK; 1601 1602 1603 mt->mparent = mparent; 1604 mt->display = display; 1605 mt->requestor = requestor; 1606 mt->sel = sel; 1607 mt->property = property; 1608 mt->selection = selection; 1609 mt->time = time; 1610 mt->index = 0; 1611 1612 retval = process_multiple (mt, False); 1613 1614 return retval; 1615} 1616 1617/* 1618 * handle_selection_request (event, sel) 1619 * 1620 * Processes a SelectionRequest event 'event' and replies to its 1621 * sender appropriately, eg. with the contents of the string 'sel'. 1622 * Returns False if a DELETE request is processed, indicating to 1623 * the calling function to delete the corresponding selection. 1624 * Returns True otherwise. 1625 */ 1626static Bool 1627handle_selection_request (XEvent event, unsigned char * sel) 1628{ 1629 XSelectionRequestEvent * xsr = &event.xselectionrequest; 1630 XSelectionEvent ev; 1631 HandleResult hr = HANDLE_OK; 1632 Bool retval = True; 1633 1634 print_debug (D_TRACE, "handle_selection_request, property=0x%x (%s), target=0x%x (%s)", 1635 xsr->property, get_atom_name (xsr->property), 1636 xsr->target, get_atom_name (xsr->target)); 1637 1638 /* Prepare a SelectionNotify event to send, either as confirmation of 1639 * placing the selection in the requested property, or as notification 1640 * that this could not be performed. */ 1641 ev.type = SelectionNotify; 1642 ev.display = xsr->display; 1643 ev.requestor = xsr->requestor; 1644 ev.selection = xsr->selection; 1645 ev.time = xsr->time; 1646 ev.target = xsr->target; 1647 1648 if (xsr->property == None && ev.target != multiple_atom) { 1649 /* Obsolete requestor */ 1650 xsr->property = xsr->target; 1651 } 1652 1653 if (ev.time != CurrentTime && ev.time < timestamp) { 1654 /* If the time is outside the period we have owned the selection, 1655 * which is any time later than timestamp, or if the requested target 1656 * is not a string, then refuse the SelectionRequest. NB. Some broken 1657 * clients don't set a valid timestamp, so we have to check against 1658 * CurrentTime here. */ 1659 ev.property = None; 1660 } else if (ev.target == timestamp_atom) { 1661 /* Return timestamp used to acquire ownership if target is TIMESTAMP */ 1662 ev.property = xsr->property; 1663 hr = handle_timestamp (ev.display, ev.requestor, ev.property, 1664 ev.selection, ev.time, NULL); 1665 } else if (ev.target == targets_atom) { 1666 /* Return a list of supported targets (TARGETS)*/ 1667 ev.property = xsr->property; 1668 hr = handle_targets (ev.display, ev.requestor, ev.property, 1669 ev.selection, ev.time, NULL); 1670 } else if (ev.target == multiple_atom) { 1671 if (xsr->property == None) { /* Invalid MULTIPLE request */ 1672 ev.property = None; 1673 } else { 1674 /* Handle MULTIPLE request */ 1675 ev.property = xsr->property; 1676 hr = handle_multiple (ev.display, ev.requestor, ev.property, sel, 1677 ev.selection, ev.time, NULL); 1678 } 1679 } else if (ev.target == XA_STRING || ev.target == text_atom) { 1680 /* Received STRING or TEXT request */ 1681 ev.property = xsr->property; 1682 hr = handle_string (ev.display, ev.requestor, ev.property, sel, 1683 ev.selection, ev.time, NULL); 1684 } else if (ev.target == utf8_atom) { 1685 /* Received UTF8_STRING request */ 1686 ev.property = xsr->property; 1687 hr = handle_utf8_string (ev.display, ev.requestor, ev.property, sel, 1688 ev.selection, ev.time, NULL); 1689 } else if (ev.target == delete_atom) { 1690 /* Received DELETE request */ 1691 ev.property = xsr->property; 1692 hr = handle_delete (ev.display, ev.requestor, ev.property); 1693 retval = False; 1694 } else { 1695 /* Cannot convert to requested target. This includes most non-string 1696 * datatypes, and INSERT_SELECTION, INSERT_PROPERTY */ 1697 ev.property = None; 1698 } 1699 1700 /* Return False if a DELETE was processed */ 1701 retval = (hr & DID_DELETE) ? False : True; 1702 1703 /* If there was an error in the transfer, it should be refused */ 1704 if (hr & HANDLE_ERR) { 1705 print_debug (D_TRACE, "Error in transfer"); 1706 ev.property = None; 1707 } 1708 1709 if ((hr & HANDLE_INCOMPLETE) == 0) { 1710 if (ev.property == None) {print_debug (D_TRACE, "Refusing conversion");} 1711 else { print_debug (D_TRACE, "Confirming conversion");} 1712 1713 XSendEvent (display, ev.requestor, False, 1714 (unsigned long)NULL, (XEvent *)&ev); 1715 1716 /* If we return False here, we may quit immediately, so sync out the 1717 * X queue. */ 1718 if (!retval) XSync (display, False); 1719 } 1720 1721 return retval; 1722} 1723 1724/* 1725 * set_selection (selection, sel) 1726 * 1727 * Takes ownership of the selection 'selection', then loops waiting for 1728 * its SelectionClear or SelectionRequest events. 1729 * 1730 * Handles SelectionRequest events, first checking for additional 1731 * input if the user has specified 'follow' mode. Returns when a 1732 * SelectionClear event is received for the specified selection. 1733 */ 1734static void 1735set_selection (Atom selection, unsigned char * sel) 1736{ 1737 XEvent event; 1738 IncrTrack * it; 1739 1740 if (own_selection (selection) == False) return; 1741 1742 for (;;) { 1743 /* Flush before unblocking signals so we send replies before exiting */ 1744 XFlush (display); 1745 unblock_exit_sigs (); 1746 XNextEvent (display, &event); 1747 block_exit_sigs (); 1748 1749 switch (event.type) { 1750 case SelectionClear: 1751 if (event.xselectionclear.selection == selection) return; 1752 break; 1753 case SelectionRequest: 1754 if (event.xselectionrequest.selection != selection) break; 1755 1756 if (do_follow) 1757 sel = read_input (sel, True); 1758 1759 if (!handle_selection_request (event, sel)) return; 1760 1761 break; 1762 case PropertyNotify: 1763 if (event.xproperty.state != PropertyDelete) break; 1764 1765 it = find_incrtrack (event.xproperty.atom); 1766 1767 if (it != NULL) { 1768 continue_incr (it); 1769 } 1770 1771 break; 1772 default: 1773 break; 1774 } 1775 } 1776} 1777 1778/* 1779 * set_selection__daemon (selection, sel) 1780 * 1781 * Creates a daemon process to handle selection requests for the 1782 * specified selection 'selection', to respond with selection text 'sel'. 1783 * If 'sel' is an empty string (NULL or "") then no daemon process is 1784 * created and the specified selection is cleared instead. 1785 */ 1786static void 1787set_selection__daemon (Atom selection, unsigned char * sel) 1788{ 1789 if (empty_string (sel) && !do_follow) { 1790 clear_selection (selection); 1791 return; 1792 } 1793 1794 become_daemon (); 1795 1796 set_selection (selection, sel); 1797} 1798 1799/* 1800 * set_selection_pair (sel_p, sel_s) 1801 * 1802 * Handles SelectionClear and SelectionRequest events for both the 1803 * primary and secondary selections. Returns once SelectionClear events 1804 * have been received for both selections. Responds to SelectionRequest 1805 * events for the primary selection with text 'sel_p' and for the 1806 * secondary selection with text 'sel_s'. 1807 */ 1808static void 1809set_selection_pair (unsigned char * sel_p, unsigned char * sel_s) 1810{ 1811 XEvent event; 1812 IncrTrack * it; 1813 1814 if (sel_p) { 1815 if (own_selection (XA_PRIMARY) == False) 1816 free_string (sel_p); 1817 } else { 1818 clear_selection (XA_PRIMARY); 1819 } 1820 1821 if (sel_s) { 1822 if (own_selection (XA_SECONDARY) == False) 1823 free_string (sel_s); 1824 } else { 1825 clear_selection (XA_SECONDARY); 1826 } 1827 1828 for (;;) { 1829 /* Flush before unblocking signals so we send replies before exiting */ 1830 XFlush (display); 1831 unblock_exit_sigs (); 1832 XNextEvent (display, &event); 1833 block_exit_sigs (); 1834 1835 switch (event.type) { 1836 case SelectionClear: 1837 if (event.xselectionclear.selection == XA_PRIMARY) { 1838 free_string (sel_p); 1839 if (sel_s == NULL) return; 1840 } else if (event.xselectionclear.selection == XA_SECONDARY) { 1841 free_string (sel_s); 1842 if (sel_p == NULL) return; 1843 } 1844 break; 1845 case SelectionRequest: 1846 if (event.xselectionrequest.selection == XA_PRIMARY) { 1847 if (!handle_selection_request (event, sel_p)) { 1848 free_string (sel_p); 1849 if (sel_s == NULL) return; 1850 } 1851 } else if (event.xselectionrequest.selection == XA_SECONDARY) { 1852 if (!handle_selection_request (event, sel_s)) { 1853 free_string (sel_s); 1854 if (sel_p == NULL) return; 1855 } 1856 } 1857 break; 1858 case PropertyNotify: 1859 if (event.xproperty.state != PropertyDelete) break; 1860 1861 it = find_incrtrack (event.xproperty.atom); 1862 1863 if (it != NULL) { 1864 continue_incr (it); 1865 } 1866 break; 1867 default: 1868 break; 1869 } 1870 } 1871} 1872 1873/* 1874 * set_selection_pair__daemon (sel_p, sel_s) 1875 * 1876 * Creates a daemon process to handle selection requests for both the 1877 * primary and secondary selections with texts 'sel_p' and 'sel_s' 1878 * respectively. 1879 * 1880 * If both 'sel_p' and 'sel_s' are empty strings (NULL or "") then no 1881 * daemon process is created, and both selections are cleared instead. 1882 */ 1883static void 1884set_selection_pair__daemon (unsigned char * sel_p, unsigned char * sel_s) 1885{ 1886 if (empty_string (sel_p) && empty_string (sel_s)) { 1887 clear_selection (XA_PRIMARY); 1888 clear_selection (XA_SECONDARY); 1889 return; 1890 } 1891 1892 become_daemon (); 1893 1894 set_selection_pair (sel_p, sel_s); 1895} 1896 1897/* 1898 * keep_selections () 1899 * 1900 * Takes ownership of both the primary and secondary selections. The current 1901 * selection texts are retrieved and a new daemon process is created to 1902 * handle both selections unmodified. 1903 */ 1904static void 1905keep_selections (void) 1906{ 1907 unsigned char * text1, * text2; 1908 1909 text1 = get_selection_text (XA_PRIMARY); 1910 text2 = get_selection_text (XA_SECONDARY); 1911 1912 set_selection_pair__daemon (text1, text2); 1913} 1914 1915/* 1916 * exchange_selections () 1917 * 1918 * Exchanges the primary and secondary selections. The current selection 1919 * texts are retrieved and a new daemon process is created to handle both 1920 * selections with their texts exchanged. 1921 */ 1922static void 1923exchange_selections (void) 1924{ 1925 unsigned char * text1, * text2; 1926 1927 text1 = get_selection_text (XA_PRIMARY); 1928 text2 = get_selection_text (XA_SECONDARY); 1929 1930 set_selection_pair__daemon (text2, text1); 1931} 1932 1933/* 1934 * free_saved_argv () 1935 * 1936 * atexit function for freeing argv, after it has been relocated to the 1937 * heap. 1938 */ 1939static void 1940free_saved_argv (void) 1941{ 1942 int i; 1943 1944 for (i=0; i < saved_argc; i++) { 1945 free (saved_argv[i]); 1946 } 1947 free (saved_argv); 1948} 1949 1950/* 1951 * expand_argv (&argc, &argv) 1952 * 1953 * Explodes single letter options so that the argument parser can see 1954 * all of them. Relocates argv and all arguments to the heap. 1955 */ 1956static void 1957expand_argv(int * argc, char **argv[]) 1958{ 1959 int i, new_i, arglen, new_argc = *argc; 1960 char ** new_argv; 1961 char * arg; 1962 1963 /* Calculate new argc */ 1964 for (i = 0; i < *argc; i++) { 1965 arglen = strlen((*argv)[i]); 1966 /* An option we need to expand? */ 1967 if ((arglen > 2) && (*argv)[i][0] == '-' && (*argv)[i][1] != '-') 1968 new_argc += arglen-2; 1969 } 1970 1971 /* Allocate new_argv */ 1972 new_argv = xs_malloc (new_argc * sizeof(char *)); 1973 1974 /* Copy args into new argv */ 1975 for (i = 0, new_i = 0; i < *argc; i++) { 1976 arglen = strlen((*argv)[i]); 1977 1978 /* An option we need to expand? */ 1979 if ((arglen > 2) 1980 && (*argv)[i][0] == '-' && (*argv)[i][1] != '-') { 1981 /* Make each letter a new argument. */ 1982 1983 char * c = ((*argv)[i] + 1); 1984 1985 while (*c != '\0') { 1986 arg = xs_malloc(sizeof(char) * 3); 1987 arg[0] = '-'; 1988 arg[1] = *c; 1989 arg[2] = '\0'; 1990 new_argv[new_i++] = arg; 1991 c++; 1992 } 1993 } else { 1994 /* Simply copy the argument pointer to new_argv */ 1995 new_argv[new_i++] = _xs_strdup ((*argv)[i]); 1996 } 1997 } 1998 1999 /* Set the expected return values */ 2000 *argc = new_argc; 2001 *argv = new_argv; 2002 2003 /* Save the new argc, argv values and free them on exit */ 2004 saved_argc = new_argc; 2005 saved_argv = new_argv; 2006 atexit (free_saved_argv); 2007} 2008 2009/* 2010 * main (argc, argv) 2011 * ================= 2012 * 2013 * Parse user options and set behaviour. 2014 * 2015 * By default the current selection is output and not modified if both 2016 * standard input and standard output are terminals (ttys). Otherwise, 2017 * the current selection is output if standard output is not a terminal 2018 * (tty), and the selection is set from standard input if standard input 2019 * is not a terminal (tty). If any input or output options are given then 2020 * the program behaves only in the requested mode. 2021 * 2022 * If both input and output is required then the previous selection is 2023 * output before being replaced by the contents of standard input. 2024 */ 2025int 2026main(int argc, char *argv[]) 2027{ 2028 Bool show_version = False; 2029 Bool show_help = False; 2030 Bool do_append = False, do_clear = False; 2031 Bool do_keep = False, do_exchange = False; 2032 Bool do_input = False, do_output = False; 2033 Bool force_input = False, force_output = False; 2034 Bool want_clipboard = False, do_delete = False; 2035 Bool trim_trailing_newline = False; 2036 Window root; 2037 Atom selection = XA_PRIMARY, test_atom; 2038 XClassHint * class_hints; 2039 int black; 2040 int i, s=0; 2041 unsigned char * old_sel = NULL, * new_sel = NULL; 2042 char * display_name = NULL; 2043 char * window_name = "xsel"; 2044 long timeout_ms = 0L; 2045 2046 zerot.it_value.tv_sec = 0; 2047 zerot.it_value.tv_usec = 0; 2048 zerot.it_interval.tv_sec = 0; 2049 zerot.it_interval.tv_usec = 0; 2050 2051 progname = argv[0]; 2052 2053 /* Specify default behaviour based on input and output file types */ 2054 if (isatty(0) && isatty(1)) { 2055 /* Solo invocation; display the selection and exit */ 2056 do_input = False; do_output = True; 2057 } else { 2058 /* Use only what is not attached to the tty */ 2059 /* Gives expected behaviour with *basic* usage of "xsel < foo", "xsel > foo", etc. */ 2060 do_input = !isatty(0); do_output = !isatty(1); 2061 } 2062 /* NOTE: 2063 * Checking stdin/stdout for being a tty is NOT reliable to tell what the user wants. 2064 * This is because child processes inherit the file descriptors of their parents; 2065 * an xsel called in a script that is e.g. daemonized (not attached to a tty), or called 2066 * with a redirection or in a pipeline will have non-tty file descriptors on default. 2067 * The redirection/piping issue also applies to "grouped" or "compound" commands 2068 * in the shell (functions, subshells, curly-brace blocks, conditionals, loops, etc.). 2069 * In all these cases, the user *must* set the mode of operation explicitly. 2070 */ 2071 2072#define OPT(s) (strcmp (argv[i], (s)) == 0) 2073 2074 /* Expand argv array before parsing to uncombine arguments. */ 2075 expand_argv(&argc, &argv); 2076 2077 /* Parse options; modify behaviour according to user-specified options */ 2078 for (i=1; i < argc; i++) { 2079 if (OPT("--help") || OPT("-h")) { 2080 show_help = True; 2081 } else if (OPT("--version")) { 2082 show_version = True; 2083 } else if (OPT("--verbose") || OPT("-v")) { 2084 debug_level++; 2085 } else if (OPT("--append") || OPT("-a")) { 2086 force_input = True; 2087 do_output = False; 2088 do_append = True; 2089 } else if (OPT("--input") || OPT("-i")) { 2090 force_input = True; 2091 do_output = False; 2092 } else if (OPT("--clear") || OPT("-c")) { 2093 do_output = False; 2094 do_clear = True; 2095 } else if (OPT("--output") || OPT("-o")) { 2096 do_input = False; 2097 force_output = True; 2098 } else if (OPT("--follow") || OPT("-f")) { 2099 force_input = True; 2100 do_output = False; 2101 do_follow = True; 2102 trim_trailing_newline = False; 2103 } else if (OPT("--zeroflush") || OPT("-z")) { 2104 force_input = True; 2105 do_output = False; 2106 do_follow = True; 2107 do_zeroflush = True; 2108 } else if (OPT("--primary") || OPT("-p")) { 2109 selection = XA_PRIMARY; 2110 } else if (OPT("--secondary") || OPT("-s")) { 2111 selection = XA_SECONDARY; 2112 } else if (OPT("--clipboard") || OPT("-b")) { 2113 want_clipboard = True; 2114 } else if (OPT("--trim")) { 2115 trim_trailing_newline = True; 2116 } else if (OPT("--keep") || OPT("-k")) { 2117 do_keep = True; 2118 } else if (OPT("--exchange") || OPT("-x")) { 2119 do_exchange = True; 2120 } else if (OPT("--display")) { 2121 i++; if (i >= argc) goto usage_err; 2122 display_name = argv[i]; 2123 } else if (OPT("--windowName")) { 2124 i++; if (i >= argc) goto usage_err; 2125 window_name = argv[i]; 2126 } else if (OPT("--selectionTimeout") || OPT("-t")) { 2127 i++; if (i >= argc) goto usage_err; 2128 timeout_ms = strtol(argv[i], (char **)NULL, 10); 2129 if (timeout_ms < 0) timeout_ms = 0; 2130 } else if (OPT("--name") || OPT("-m")) { 2131 i++; if (i >= argc) goto usage_err; 2132 window_name = argv[i]; 2133 } else if (OPT("--nodetach") || OPT("-n")) { 2134 no_daemon = True; 2135 } else if (OPT("--delete") || OPT("-d")) { 2136 do_output = False; 2137 do_delete = True; 2138 } else if (OPT("--logfile") || OPT("-l")) { 2139 i++; if (i >= argc) goto usage_err; 2140 _xs_strncpy (logfile, argv[i], MAXFNAME); 2141 } else { 2142 goto usage_err; 2143 } 2144 } 2145 2146 if (show_version) { 2147 printf ("xsel version " VERSION " by " AUTHOR "\n"); 2148 } 2149 2150 if (show_help) { 2151 usage (); 2152 } 2153 2154 if (show_version || show_help) { 2155 exit (0); 2156 } 2157 2158 if (do_input || force_input) { 2159 if (fstat (0, &in_statbuf) == -1) { 2160 exit_err ("fstat error on stdin"); 2161 } 2162 if (S_ISDIR(in_statbuf.st_mode)) { 2163 exit_err ("-: Is a directory\n"); 2164 } 2165 } 2166 2167 if (do_output || force_output) { 2168 if (fstat (1, &out_statbuf) == -1) { 2169 exit_err ("fstat error on stdout"); 2170 } 2171 if (S_ISDIR(out_statbuf.st_mode)) { 2172 exit_err ("stdout: Is a directory\n"); 2173 } 2174 } 2175 2176 timeout = timeout_ms * 1000; 2177 2178 display = XOpenDisplay (display_name); 2179 if (display==NULL) { 2180 exit_err ("Can't open display: %s\n", 2181 display_name ? display_name : "(null)"); 2182 } 2183 root = XDefaultRootWindow (display); 2184 2185 /* Create an unmapped window for receiving events */ 2186 black = BlackPixel (display, DefaultScreen (display)); 2187 window = XCreateSimpleWindow (display, root, 0, 0, 1, 1, 0, black, black); 2188 2189 print_debug (D_INFO, "Window id: 0x%x (unmapped)", window); 2190 2191 if (window_name != NULL) { 2192 XStoreName (display, window, window_name); 2193 print_debug (D_INFO, "The name %s is assigned to the window", window_name); 2194 } 2195 2196 /* Set window name and class */ 2197 XStoreName(display, window, window_name); 2198 2199 class_hints = XAllocClassHint(); 2200 if (class_hints==NULL) { 2201 exit_err ("Can't allocate class hints memory\n"); 2202 } 2203 class_hints->res_name = "xsel"; 2204 class_hints->res_class = "XSel"; 2205 XSetClassHint(display, window, class_hints); 2206 XFree(class_hints); 2207 2208 /* Get a timestamp */ 2209 XSelectInput (display, window, PropertyChangeMask); 2210 timestamp = get_timestamp (); 2211 2212 print_debug (D_OBSC, "Timestamp: %lu", timestamp); 2213 2214 /* Get the maximum incremental selection size in bytes */ 2215 /*max_req = MAX_SELECTION_INCR (display);*/ 2216 max_req = 4000; 2217 2218 print_debug (D_OBSC, "Maximum request size: %ld bytes", max_req); 2219 2220 /* Consistency check */ 2221 test_atom = XInternAtom (display, "PRIMARY", False); 2222 if (test_atom != XA_PRIMARY) 2223 print_debug (D_WARN, "XA_PRIMARY not named \"PRIMARY\"\n"); 2224 test_atom = XInternAtom (display, "SECONDARY", False); 2225 if (test_atom != XA_SECONDARY) 2226 print_debug (D_WARN, "XA_SECONDARY not named \"SECONDARY\"\n"); 2227 2228 NUM_TARGETS=0; 2229 2230 /* Get the TIMESTAMP atom */ 2231 timestamp_atom = XInternAtom (display, "TIMESTAMP", False); 2232 supported_targets[s++] = timestamp_atom; 2233 NUM_TARGETS++; 2234 2235 /* Get the MULTIPLE atom */ 2236 multiple_atom = XInternAtom (display, "MULTIPLE", False); 2237 supported_targets[s++] = multiple_atom; 2238 NUM_TARGETS++; 2239 2240 /* Get the TARGETS atom */ 2241 targets_atom = XInternAtom (display, "TARGETS", False); 2242 supported_targets[s++] = targets_atom; 2243 NUM_TARGETS++; 2244 2245 /* Get the DELETE atom */ 2246 delete_atom = XInternAtom (display, "DELETE", False); 2247 supported_targets[s++] = delete_atom; 2248 NUM_TARGETS++; 2249 2250 /* Get the INCR atom */ 2251 incr_atom = XInternAtom (display, "INCR", False); 2252 supported_targets[s++] = incr_atom; 2253 NUM_TARGETS++; 2254 2255 /* Get the TEXT atom */ 2256 text_atom = XInternAtom (display, "TEXT", False); 2257 supported_targets[s++] = text_atom; 2258 NUM_TARGETS++; 2259 2260 /* Get the UTF8_STRING atom */ 2261 utf8_atom = XInternAtom (display, "UTF8_STRING", True); 2262 if(utf8_atom != None) { 2263 supported_targets[s++] = utf8_atom; 2264 NUM_TARGETS++; 2265 } else { 2266 utf8_atom = XA_STRING; 2267 } 2268 2269 supported_targets[s++] = XA_STRING; 2270 NUM_TARGETS++; 2271 2272 if (NUM_TARGETS > MAX_NUM_TARGETS) { 2273 exit_err ("internal error num-targets (%d) > max-num-targets (%d)\n", 2274 NUM_TARGETS, MAX_NUM_TARGETS); 2275 } 2276 2277 /* Get the NULL atom */ 2278 null_atom = XInternAtom (display, "NULL", False); 2279 2280 /* Get the COMPOUND_TEXT atom. 2281 * NB. We do not currently serve COMPOUND_TEXT; we can retrieve it but 2282 * do not perform charset conversion. 2283 */ 2284 compound_text_atom = XInternAtom (display, "COMPOUND_TEXT", False); 2285 2286 sigemptyset (&exit_sigs); 2287 sigaddset (&exit_sigs, SIGALRM); 2288 sigaddset (&exit_sigs, SIGINT); 2289 sigaddset (&exit_sigs, SIGTERM); 2290 2291 /* handle selection keeping and exit if so */ 2292 if (do_keep) { 2293 keep_selections (); 2294 _exit (0); 2295 } 2296 2297 /* handle selection exchange and exit if so */ 2298 if (do_exchange) { 2299 exchange_selections (); 2300 _exit (0); 2301 } 2302 2303 /* Find the "CLIPBOARD" selection if required */ 2304 if (want_clipboard) { 2305 selection = XInternAtom (display, "CLIPBOARD", False); 2306 } 2307 2308 /* handle output modes */ 2309 if (do_output || force_output) { 2310 /* Get the current selection */ 2311 old_sel = get_selection_text (selection); 2312 if (old_sel) { 2313 2314 if (trim_trailing_newline) { 2315 unsigned int old_sel_len = xs_strlen(old_sel); 2316 if (old_sel[old_sel_len - 1 ] == '\n') { 2317 old_sel[old_sel_len - 1] = '\0'; 2318 } 2319 } 2320 2321 printf ("%s", old_sel); 2322 if (!do_append && *old_sel != '\0' && isatty(1) && 2323 old_sel[xs_strlen (old_sel) - 1] != '\n') { 2324 fflush (stdout); 2325 } 2326 } 2327 } 2328 2329 /* handle input and clear modes */ 2330 if (do_delete) { 2331 get_selection (selection, delete_atom); 2332 } else if (do_clear) { 2333 clear_selection (selection); 2334 } 2335 else if (do_input || force_input) { 2336 if (do_output || force_output) fflush (stdout); 2337 if (do_append) { 2338 if (!old_sel) old_sel = get_selection_text (selection); 2339 new_sel = copy_sel (old_sel); 2340 } 2341 new_sel = initialise_read (new_sel); 2342 if(!do_follow) 2343 new_sel = read_input (new_sel, False); 2344 2345 if(trim_trailing_newline) { 2346 unsigned int sel_len = xs_strlen(new_sel); 2347 if (new_sel[sel_len - 1 ] == '\n') { 2348 new_sel[sel_len - 1] = '\0'; 2349 } 2350 } 2351 2352 set_selection__daemon (selection, new_sel); 2353 } 2354 2355 exit (0); 2356 2357usage_err: 2358 usage (); 2359 exit (0); 2360}