xsel

Program for manipulating the X clipboard
git clone https://git.sinitax.com/kfish/xsel
Log | Files | Refs | README | LICENSE | sfeed.txt

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 *)&timestamp, 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}