root/xsel/trunk/xsel.c

Revision 249, 59.7 kB (checked in by conrad, 8 months ago)

When stdout is a tty, use stderr to ensure there is a newline before
the shell prompt.

(Modified) patch from Yair K.:

If the used selection does not end with a newline, then:

A) If 'xsel -o' than the command prompt may overwrite the last line (depends

on $PS1, shell, etc. but I think quite likely to happen)

B) 'xsel -a -o -i' doesn't display the last line (on Linux at least). The

last line will still exist after appending, but it isn't displayed. This is
(I suspect) because stdout is line-buffered, so stdout isn't flushed yet.

Line 
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
35 #include "xsel.h"
36
37
38 /* The name we were invoked as (argv[0]) */
39 static char * progname;
40
41 /* Verbosity level for debugging */
42 static int debug_level = DEBUG_LEVEL;
43
44 /* Our X Display and Window */
45 static Display * display;
46 static Window window;
47
48 /* Maxmimum request size supported by this X server */
49 static long max_req;
50
51 /* Our timestamp for all operations */
52 static Time timestamp;
53
54 static Atom timestamp_atom; /* The TIMESTAMP atom */
55 static Atom multiple_atom; /* The MULTIPLE atom */
56 static Atom targets_atom; /* The TARGETS atom */
57 static Atom delete_atom; /* The DELETE atom */
58 static Atom incr_atom; /* The INCR atom */
59 static Atom null_atom; /* The NULL atom */
60 static Atom text_atom; /* The TEXT atom */
61 static Atom utf8_atom; /* The UTF8 atom */
62 static Atom compound_text_atom; /* The COMPOUND_TEXT atom */
63
64 /* Number of selection targets served by this.
65  * (MULTIPLE, INCR, TARGETS, TIMESTAMP, DELETE, TEXT, UTF8_STRING and STRING)
66  * NB. We do not currently serve COMPOUND_TEXT; we can retrieve it but do not
67  * perform charset conversion.
68  */
69 #define MAX_NUM_TARGETS 8
70 static int NUM_TARGETS;
71 static Atom supported_targets[MAX_NUM_TARGETS];
72
73 /* do_follow: Follow mode for output */
74 static Bool do_follow = False;
75
76 /* nodaemon: Disable daemon mode if True. */
77 static Bool no_daemon = False;
78
79 /* logfile: name of file to log error messages to when detached */
80 static char logfile[MAXFNAME];
81
82 /* fstat() on stdin and stdout */
83 static struct stat in_statbuf, out_statbuf;
84
85 static int total_input = 0;
86 static int current_alloc = 0;
87
88 static long timeout = 0;
89 static struct itimerval timer;
90
91 static int saved_argc;
92 static char ** saved_argv;
93
94 /*
95  * usage ()
96  *
97  * print usage information.
98  */
99 static void
100 usage (void)
101 {
102   printf ("Usage: xsel [options]\n");
103   printf ("Manipulate the X selection.\n\n");
104   printf ("By default the current selection is output and not modified if both\n");
105   printf ("standard input and standard output are terminals (ttys).  Otherwise,\n");
106   printf ("the current selection is output if standard output is not a terminal\n");
107   printf ("(tty), and the selection is set from standard input if standard input\n");
108   printf ("is not a terminal (tty). If any input or output options are given then\n");
109   printf ("the program behaves only in the requested mode.\n\n");
110   printf ("If both input and output is required then the previous selection is\n");
111   printf ("output before being replaced by the contents of standard input.\n\n");
112   printf ("Input options\n");
113   printf ("  -a, --append          Append standard input to the selection\n");
114   printf ("  -f, --follow          Append to selection as standard input grows\n");
115   printf ("  -i, --input           Read standard input into the selection\n\n");
116   printf ("Output options\n");
117   printf ("  -o, --output          Write the selection to standard output\n\n");
118   printf ("Action options\n");
119   printf ("  -c, --clear           Clear the selection\n");
120   printf ("  -d, --delete          Request that the selection be cleared and that\n");
121   printf ("                        the application owning it delete its contents\n\n");
122   printf ("Selection options\n");
123   printf ("  -p, --primary         Operate on the PRIMARY selection (default)\n");
124   printf ("  -s, --secondary       Operate on the SECONDARY selection\n");
125   printf ("  -b, --clipboard       Operate on the CLIPBOARD selection\n\n");
126   printf ("  -k, --keep            Do not modify the selections, but make the PRIMARY\n");
127   printf ("                        and SECONDARY selections persist even after the\n");
128   printf ("                        programs they were selected in exit.\n");
129   printf ("  -x, --exchange        Exchange the PRIMARY and SECONDARY selections\n\n");
130   printf ("X options\n");
131   printf ("  --display displayname\n");
132   printf ("                        Specify the connection to the X server\n");
133   printf ("  -t ms, --selectionTimeout ms\n");
134   printf ("                        Specify the timeout in milliseconds within which the\n");
135   printf ("                        selection must be retrieved. A value of 0 (zero)\n");
136   printf ("                        specifies no timeout (default)\n\n");
137   printf ("Miscellaneous options\n");
138   printf ("  -l, --logfile         Specify file to log errors to when detached.\n");
139   printf ("  -n, --nodetach        Do not detach from the controlling terminal. Without\n");
140   printf ("                        this option, xsel will fork to become a background\n");
141   printf ("                        process in input, exchange and keep modes.\n\n");
142   printf ("  -h, --help            Display this help and exit\n");
143   printf ("  -v, --verbose         Print informative messages\n");
144   printf ("  --version             Output version information and exit\n\n");
145   printf ("Please report bugs to <conrad@vergenet.net>.\n");
146 }
147
148 /*
149  * exit_err (fmt)
150  *
151  * Print a formatted error message and errno information to stderr,
152  * then exit with return code 1.
153  */
154 static void
155 exit_err (const char * fmt, ...)
156 {
157   va_list ap;
158   int errno_save;
159   char buf[MAXLINE];
160   int n;
161
162   errno_save = errno;
163
164   va_start (ap, fmt);
165
166   snprintf (buf, MAXLINE, "%s: ", progname);
167   n = strlen (buf);
168
169   vsnprintf (buf+n, MAXLINE-n, fmt, ap);
170   n = strlen (buf);
171
172   snprintf (buf+n, MAXLINE-n, ": %s\n", strerror (errno_save));
173
174   fflush (stdout); /* in case stdout and stderr are the same */
175   fputs (buf, stderr);
176   fflush (NULL);
177
178   va_end (ap);
179   exit (1);
180 }
181
182 /*
183  * print_err (fmt)
184  *
185  * Print a formatted error message to stderr.
186  */
187 static void
188 print_err (const char * fmt, ...)
189 {
190   va_list ap;
191   int errno_save;
192   char buf[MAXLINE];
193   int n;
194
195   errno_save = errno;
196
197   va_start (ap, fmt);
198
199   snprintf (buf, MAXLINE, "%s: ", progname);
200   n = strlen (buf);
201
202   vsnprintf (buf+n, MAXLINE-n, fmt, ap);
203   n = strlen (buf);
204
205   fflush (stdout); /* in case stdout and stderr are the same */
206   fputs (buf, stderr);
207   fputc ('\n', stderr);
208   fflush (NULL);
209
210   va_end (ap);
211 }
212
213 /*
214  * print_debug (level, fmt)
215  *
216  * Print a formatted debugging message of level 'level' to stderr
217  */
218 #define print_debug(x,y...) {if (x <= debug_level) print_err (y);}
219
220 /*
221  * get_atom_name (atom)
222  *
223  * Returns a string with a printable name for the Atom 'atom'.
224  */
225 static char *
226 get_atom_name (Atom atom)
227 {
228   char * ret;
229   static char atom_name[MAXLINE+1];
230
231   if (atom == None) return "None";
232   if (atom == XA_STRING) return "STRING";
233   if (atom == XA_PRIMARY) return "PRIMARY";
234   if (atom == XA_SECONDARY) return "SECONDARY";
235   if (atom == timestamp_atom) return "TIMESTAMP";
236   if (atom == multiple_atom) return "MULTIPLE";
237   if (atom == targets_atom) return "TARGETS";
238   if (atom == delete_atom) return "DELETE";
239   if (atom == incr_atom) return "INCR";
240   if (atom == null_atom) return "NULL";
241   if (atom == text_atom) return "TEXT";
242   if (atom == utf8_atom) return "UTF8_STRING";
243
244   ret = XGetAtomName (display, atom);
245   strncpy (atom_name, ret, sizeof (atom_name));
246   if (atom_name[MAXLINE] != '\0')
247     {
248       atom_name[MAXLINE-3] = '.';
249       atom_name[MAXLINE-2] = '.';
250       atom_name[MAXLINE-1] = '.';
251       atom_name[MAXLINE] = '\0';
252     }
253   XFree (ret);
254
255   return atom_name;
256 }
257
258 /*
259  * debug_property (level, requestor, property, target, length)
260  *
261  * Print debugging information (at level 'level') about a property received.
262  */
263 static void
264 debug_property (int level, Window requestor, Atom property, Atom target,
265                 unsigned long length)
266 {
267   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);
268 }
269
270 /*
271  * xs_malloc (size)
272  *
273  * Malloc wrapper. Always returns a successful allocation. Exits if the
274  * allocation didn't succeed.
275  */
276 static void *
277 xs_malloc (size_t size)
278 {
279   void * ret;
280
281   if (size == 0) size = 1;
282   if ((ret = malloc (size)) == NULL) {
283     exit_err ("malloc error");
284   }
285
286   return ret;
287 }
288
289 /*
290  * xs_strdup (s)
291  *
292  * strdup wrapper for unsigned char *
293  */
294 #define xs_strdup(s) ((unsigned char *) _xs_strdup ((const char *)s))
295 static char * _xs_strdup (const char * s)
296 {
297   char * ret;
298
299   if (s == NULL) return NULL;
300   if ((ret = strdup(s)) == NULL) {
301     exit_err ("strdup error");
302   }
303
304   return ret;
305 }
306
307 /*
308  * xs_strlen (s)
309  *
310  * strlen wrapper for unsigned char *
311  */
312 #define xs_strlen(s) (strlen ((const char *) s))
313
314 /*
315  * xs_strncpy (s)
316  *
317  * strncpy wrapper for unsigned char *
318  */
319 #define xs_strncpy(dest,s,n) (_xs_strncpy ((char *)dest, (const char *)s, n))
320 static char *
321 _xs_strncpy (char * dest, const char * src, size_t n)
322 {
323   if (n > 0) {
324     strncpy (dest, src, n);
325     dest[n-1] = '\0';
326   }
327   return dest;
328 }
329
330 /*
331  * get_homedir ()
332  *
333  * Get the user's home directory.
334  */
335 static char *
336 get_homedir (void)
337 {
338   uid_t uid;
339   char * username, * homedir;
340   struct passwd * pw;
341
342   if ((homedir = getenv ("HOME")) != NULL) {
343     return homedir;
344   }
345
346   /* else ... go hunting for it */
347   uid = getuid ();
348
349   username = getenv ("LOGNAME");
350   if (!username) username = getenv ("USER");
351
352   if (username) {
353     pw = getpwnam (username);
354     if (pw && pw->pw_uid == uid) goto gotpw;
355   }
356
357   pw = getpwuid (uid);
358
359 gotpw:
360
361   if (!pw) {
362     exit_err ("error retrieving passwd entry");
363   }
364
365   homedir = _xs_strdup (pw->pw_dir);
366
367   return homedir;
368 }
369
370 /*
371  * become_daemon ()
372  *
373  * Perform the required procedure to become a daemon process, as
374  * outlined in the Unix programming FAQ:
375  * http://www.steve.org.uk/Reference/Unix/faq_2.html#SEC16
376  * and open a logfile.
377  */
378 static void
379 become_daemon (void)
380 {
381   pid_t pid;
382   int null_r_fd, null_w_fd, log_fd;
383   char * homedir;
384
385   if (no_daemon) return;
386
387   homedir = get_homedir ();
388
389   /* Check that we can open a logfile before continuing */
390
391   /* If the user has specified a --logfile, use that ... */
392   if (logfile[0] == '\0') {
393     /* ... otherwise use the default logfile */
394     snprintf (logfile, MAXFNAME, "%s/.xsel.log", homedir);
395   }
396
397   /* Make sure to create the logfile with sane permissions */
398   log_fd = open (logfile, O_WRONLY|O_APPEND|O_CREAT, 0600);
399   if (log_fd == -1) {
400     exit_err ("error opening logfile %s for writing", logfile);
401   }
402   print_debug (D_INFO, "opened logfile %s", logfile);
403
404   if ((pid = fork()) == -1) {
405     exit_err ("error forking");
406   } else if (pid > 0) {
407     _exit (0);
408   }
409
410   if (setsid () == -1) {
411     exit_err ("setsid error");
412   }
413
414   if ((pid = fork()) == -1) {
415     exit_err ("error forking");
416   } else if (pid > 0) {
417     _exit (0);
418   }
419
420   umask (0);
421
422   if (chdir (homedir) == -1) {
423     print_debug (D_WARN, "Could not chdir to %s\n", homedir);
424     if (chdir ("/") == -1) {
425       exit_err ("Error chdir to /");
426     }
427   }
428
429   /* dup2 /dev/null on stdin unless following input */
430   if (!do_follow) {
431     null_r_fd = open ("/dev/null", O_RDONLY);
432     if (null_r_fd == -1) {
433       exit_err ("error opening /dev/null for reading");
434     }
435     if (dup2 (null_r_fd, 0) == -1) {
436       exit_err ("error duplicating /dev/null on stdin");
437     }
438   }
439
440   /* dup2 /dev/null on stdout */
441   null_w_fd = open ("/dev/null", O_WRONLY|O_APPEND);
442   if (null_w_fd == -1) {
443     exit_err ("error opening /dev/null for writing");
444   }
445   if (dup2 (null_w_fd, 1) == -1) {
446     exit_err ("error duplicating /dev/null on stdout");
447   }
448
449   /* dup2 logfile on stderr */
450   if (dup2 (log_fd, 2) == -1) {
451     exit_err ("error duplicating logfile %s on stderr", logfile);
452   }
453 }
454
455 /*
456  * get_timestamp ()
457  *
458  * Get the current X server time.
459  *
460  * This is done by doing a zero-length append to a random property of the
461  * window, and checking the time on the subsequent PropertyNotify event.
462  *
463  * PRECONDITION: the window must have PropertyChangeMask set.
464  */
465 static Time
466 get_timestamp (void)
467 {
468   XEvent event;
469
470   XChangeProperty (display, window, XA_WM_NAME, XA_STRING, 8,
471                    PropModeAppend, NULL, 0);
472
473   while (1) {
474     XNextEvent (display, &event);
475
476     if (event.type == PropertyNotify)
477       return event.xproperty.time;
478   }
479 }
480
481 /*
482  * SELECTION RETRIEVAL
483  * ===================
484  *
485  * The following functions implement retrieval of an X selection,
486  * optionally within a user-specified timeout.
487  *
488  *
489  * Selection timeout handling.
490  * ---------------------------
491  *
492  * The selection retrieval can time out if no response is received within
493  * a user-specified time limit. In order to ensure we time the entire
494  * selection retrieval, we use an interval timer and catch SIGVTALRM.
495  * [Calling select() on the XConnectionNumber would only provide a timeout
496  * to the first XEvent.]
497  */
498
499 /* The jmp_buf to longjmp out of the signal handler */
500 static sigjmp_buf env_alrm;
501
502 /*
503  * alarm_handler (sig)
504  *
505  * Signal handler for catching SIGVTALRM.
506  */
507 static void
508 alarm_handler (int sig)
509 {
510   siglongjmp (env_alrm, 1);
511 }
512
513 /*
514  * get_append_property ()
515  *
516  * Get a window property and append its data to a buffer at a given offset
517  * pointed to by *offset. 'offset' is modified by this routine to point to
518  * the end of the data.
519  *
520  * Returns True if more data is available for receipt.
521  *
522  * If an error is encountered, the buffer is free'd.
523  */
524 static Bool
525 get_append_property (XSelectionEvent * xsl, unsigned char ** buffer,
526                      unsigned long * offset, unsigned long * alloc)
527 {
528   unsigned char * ptr;
529   Atom target;
530   int format;
531   unsigned long bytesafter, length;
532   unsigned char * value;
533
534   XGetWindowProperty (xsl->display, xsl->requestor, xsl->property,
535                       0L, 1000000, True, (Atom)AnyPropertyType,
536                       &target, &format, &length, &bytesafter, &value);
537
538   debug_property (D_TRACE, xsl->requestor, xsl->property, target, length);
539
540   if (target != XA_STRING) {
541     print_debug (D_OBSC, "target %s not XA_STRING in get_append_property()",
542                  get_atom_name (target));
543     free (*buffer);
544     *buffer = NULL;
545     return False;
546   } else if (length == 0) {
547     /* A length of 0 indicates the end of the transfer */
548     print_debug (D_TRACE, "Got zero length property; end of INCR transfer");
549     return False;
550   } else if (format == 8) {
551     if (*offset + length > *alloc) {
552       *alloc = *offset + length;
553       if ((*buffer = realloc (*buffer, *alloc)) == NULL) {
554         exit_err ("realloc error");
555       }
556     }
557     ptr = *buffer + *offset;
558     xs_strncpy (ptr, value, length);
559     *offset += length;
560     print_debug (D_TRACE, "Appended %d bytes to buffer\n", length);
561   } else {
562     print_debug (D_WARN, "Retrieved non-8-bit data\n");
563   }
564
565   return True;
566 }
567
568
569 /*
570  * wait_incr_selection (selection)
571  *
572  * Retrieve a property of target type INCR. Perform incremental retrieval
573  * and return the resulting data.
574  */
575 static unsigned char *
576 wait_incr_selection (Atom selection, XSelectionEvent * xsl, int init_alloc)
577 {
578   XEvent event;
579   unsigned char * incr_base = NULL, * incr_ptr = NULL;
580   unsigned long incr_alloc = 0, incr_xfer = 0;
581   Bool wait_prop = True;
582
583   print_debug (D_TRACE, "Initialising incremental retrieval of at least %d bytes\n", init_alloc);
584
585   /* Take an interest in the requestor */
586   XSelectInput (xsl->display, xsl->requestor, PropertyChangeMask);
587
588   incr_alloc = init_alloc;
589   incr_base = xs_malloc (incr_alloc);
590   incr_ptr = incr_base;
591
592   print_debug (D_TRACE, "Deleting property that informed of INCR transfer");
593   XDeleteProperty (xsl->display, xsl->requestor, xsl->property);
594
595   print_debug (D_TRACE, "Waiting on PropertyNotify events");
596   while (wait_prop) {
597     XNextEvent (xsl->display, &event);
598
599     switch (event.type) {
600     case PropertyNotify:
601       if (event.xproperty.state != PropertyNewValue) break;
602
603       wait_prop = get_append_property (xsl, &incr_base, &incr_xfer,
604                                        &incr_alloc);
605       break;
606     default:
607       break;
608     }
609   }
610
611   /* when zero length found, finish up & delete last */
612   XDeleteProperty (xsl->display, xsl->requestor, xsl->property);
613
614   print_debug (D_TRACE, "Finished INCR retrieval");
615
616   return incr_base;
617 }
618
619 /*
620  * wait_selection (selection, request_target)
621  *
622  * Block until we receive a SelectionNotify event, and return its
623  * contents; or NULL in the case of a deletion or error. This assumes we
624  * have already called XConvertSelection, requesting a string (explicitly
625  * XA_STRING) or deletion (delete_atom).
626  */
627 static unsigned char *
628 wait_selection (Atom selection, Atom request_target)
629 {
630   XEvent event;
631   Atom target;
632   int format;
633   unsigned long bytesafter, length;
634   unsigned char * value, * retval = NULL;
635   Bool keep_waiting = True;
636
637   while (keep_waiting) {
638     XNextEvent (display, &event);
639
640     switch (event.type) {
641     case SelectionNotify:
642       if (event.xselection.selection != selection) break;
643
644       if (event.xselection.property == None) {
645         print_debug (D_WARN, "Conversion refused");
646         value = NULL;
647         keep_waiting = False;
648       } else if (event.xselection.property == null_atom &&
649                  request_target == delete_atom) {
650       } else {
651         XGetWindowProperty (event.xselection.display,
652                             event.xselection.requestor,
653                             event.xselection.property, 0L, 1000000,
654                             False, (Atom)AnyPropertyType, &target,
655                             &format, &length, &bytesafter, &value);
656
657         debug_property (D_TRACE, event.xselection.requestor,
658                         event.xselection.property, target, length);
659
660         if (request_target == delete_atom && value == NULL) {
661           keep_waiting = False;
662         } else if (target == incr_atom) {
663           /* Handle INCR transfers */
664           retval = wait_incr_selection (selection, &event.xselection,
665                                         *(int *)value);
666           keep_waiting = False;
667         } else if (target != utf8_atom && target != XA_STRING &&
668                    target != compound_text_atom &&
669                    request_target != delete_atom) {
670           /* Report non-TEXT atoms */
671           print_debug (D_WARN, "Selection (type %s) is not a string.",
672                        get_atom_name (target));
673           free (retval);
674           retval = NULL;
675           keep_waiting = False;
676         } else {
677           retval = xs_strdup (value);
678           XFree (value);
679           keep_waiting = False;
680         }
681
682         XDeleteProperty (event.xselection.display,
683                          event.xselection.requestor,
684                          event.xselection.property);
685
686       }
687       break;
688     default:
689       break;
690     }
691   }
692
693   /* Now that we've received the SelectionNotify event, clear any
694    * remaining timeout. */
695   if (timeout > 0) {
696     setitimer (ITIMER_VIRTUAL, (struct itimerval *)0, (struct itimerval *)0);
697   }
698
699   return retval;
700 }
701
702 /*
703  * get_selection (selection, request_target)
704  *
705  * Retrieves the specified selection and returns its value.
706  *
707  * If a non-zero timeout is specified then set a virtual interval
708  * timer. Return NULL and print an error message if the timeout
709  * expires before the selection has been retrieved.
710  */
711 static unsigned char *
712 get_selection (Atom selection, Atom request_target)
713 {
714   Atom prop;
715   unsigned char * retval;
716
717   prop = XInternAtom (display, "XSEL_DATA", False);
718   XConvertSelection (display, selection, request_target, prop, window,
719                      timestamp);
720   XSync (display, False);
721
722   if (timeout > 0) {
723     if (signal (SIGVTALRM, alarm_handler) == SIG_ERR) {
724       exit_err ("error setting timeout handler");
725     }
726
727     timer.it_interval.tv_sec = 0;
728     timer.it_interval.tv_usec = timeout;
729     timer.it_value.tv_sec = 0;
730     timer.it_value.tv_usec = timeout;
731
732     if (sigsetjmp (env_alrm, 0) == 0) {
733       setitimer (ITIMER_VIRTUAL, &timer, (struct itimerval *)0);
734       retval = wait_selection (selection, request_target);
735     } else {
736       print_debug (D_WARN, "selection timed out");
737       retval = NULL;
738     }
739   } else {
740     retval = wait_selection (selection, request_target);
741   }
742
743   return retval;
744 }
745
746 /*
747  * get_selection_text (Atom selection)
748  *
749  * Retrieve a text selection. First attempt to retrieve it as UTF_STRING,
750  * and if that fails attempt to retrieve it as a plain XA_STRING.
751  *
752  * NB. Before implementing this, an attempt was made to query TARGETS and
753  * request UTF8_STRING only if listed there, as described in:
754  * http://www.pps.jussieu.fr/~jch/software/UTF8_STRING/UTF8_STRING.text
755  * However, that did not seem to work reliably when tested against various
756  * applications (eg. Mozilla Firefox). This method is of course more
757  * reliable.
758  */
759 static unsigned char *
760 get_selection_text (Atom selection)
761 {
762   unsigned char * retval;
763
764   if ((retval = get_selection (selection, utf8_atom)) == NULL)
765     retval = get_selection (selection, XA_STRING);
766
767   return retval;
768 }
769
770
771 /*
772  * SELECTION SETTING
773  * =================
774  *
775  * The following functions allow a given selection to be set, appended to
776  * or cleared, or to exchange the primary and secondary selections.
777  */
778
779 /*
780  * copy_sel (s)
781  *
782  * Copy a string into a new selection buffer, and intitialise
783  * current_alloc and total_input to exactly its length.
784  */
785 static unsigned char *
786 copy_sel (unsigned char * s)
787 {
788   unsigned char * new_sel = NULL;
789
790   new_sel = xs_strdup (s);
791   current_alloc = total_input = xs_strlen (s);
792
793   return new_sel;
794 }
795
796 /*
797  * read_input (read_buffer, do_select)
798  *
799  * Read input from stdin into the specified read_buffer.
800  *
801  * read_buffer must have been dynamically allocated before calling this
802  * function, or be NULL. Input is read until end-of-file is reached, and
803  * read_buffer will be reallocated to accomodate the entire contents of
804  * the input. read_buffer, which may have been reallocated, is returned
805  * upon completion.
806  *
807  * If 'do_select' is True, this function will first check if any data
808  * is available for reading, and return immediately if not.
809  */
810 static unsigned char *
811 read_input (unsigned char * read_buffer, Bool do_select)
812 {
813   int insize = in_statbuf.st_blksize;
814   unsigned char * new_buffer = NULL;
815   int d, fatal = 0, nfd;
816   ssize_t n;
817   fd_set fds;
818   struct timeval select_timeout;
819
820   if (do_select) {
821 try_read:
822     /* Check if data is available for reading -- if not, return immediately */
823     FD_ZERO (&fds);
824     FD_SET (0, &fds);
825
826     select_timeout.tv_sec = (time_t)0;
827     select_timeout.tv_usec = (time_t)0;
828
829     nfd = select (1, &fds, NULL, NULL, &select_timeout);
830     if (nfd == -1) {
831       if (errno == EINTR) goto try_read;
832       else exit_err ("select error");
833     } else if (nfd == 0) {
834       print_debug (D_TRACE, "No data available for reading");
835       return read_buffer;
836     }
837   }
838
839   do {
840     /* check if buffer is full */
841     if (current_alloc == total_input) {
842       if ((d = (current_alloc % insize)) != 0) current_alloc += (insize-d);
843       current_alloc *= 2;
844       new_buffer = realloc (read_buffer, current_alloc);
845       if (new_buffer == NULL) {
846         exit_err ("realloc error");
847       }
848       read_buffer = new_buffer;
849     }
850
851     /* read the remaining data, up to the optimal block length */
852     n = read (0, &read_buffer[total_input],
853               MIN(current_alloc - total_input, insize));
854     if (n == -1) {
855       switch (errno) {
856       case EAGAIN:
857       case EINTR:
858         break;
859       default:
860         perror ("read error");
861         fatal = 1;
862         break;
863       }
864     }
865     total_input += n;
866   } while (n != 0 && !fatal);
867
868   read_buffer[total_input] = '\0';
869
870   print_debug (D_TRACE, "Accumulated %d bytes input", total_input);
871
872   return read_buffer;
873 }
874
875 /*
876  * initialise_read (read_buffer)
877  *
878  * Initialises the read_buffer and the state variable current_alloc.
879  * read_buffer is reallocated to accomodate either the entire input
880  * if stdin is a regular file, or at least one block of input otherwise.
881  * If the supplied read_buffer is NULL, a new buffer will be allocated.
882  */
883 static unsigned char *
884 initialise_read (unsigned char * read_buffer)
885 {
886   int insize = in_statbuf.st_blksize;
887   unsigned char * new_buffer = NULL;
888
889   if (S_ISREG (in_statbuf.st_mode)) {
890     current_alloc += in_statbuf.st_size;
891   } else {
892     current_alloc += insize;
893   }
894
895   if ((new_buffer = realloc (read_buffer, current_alloc)) == NULL) {
896     exit_err ("realloc error");
897   }
898
899   read_buffer = new_buffer;
900
901   return read_buffer;
902 }
903
904 /* Forward declaration of refuse_all_incr () */
905 static void
906 refuse_all_incr (void);
907
908 /*
909  * handle_x_errors ()
910  *
911  * XError handler.
912  */
913 static int
914 handle_x_errors (Display * display, XErrorEvent * eev)
915 {
916   char err_buf[MAXLINE];
917
918   /* Make sure to send a refusal to all waiting INCR requests
919    * and delete the corresponding properties. */
920   if (eev->error_code == BadAlloc) refuse_all_incr ();
921
922   XGetErrorText (display, eev->error_code, err_buf, MAXLINE);
923   exit_err (err_buf);
924
925   return 0;
926 }
927
928 /*
929  * clear_selection (selection)
930  *
931  * Clears the specified X selection 'selection'. This requests that no
932  * process should own 'selection'; thus the X server will respond to
933  * SelectionRequests with an empty property and we don't need to leave
934  * a daemon hanging around to service this selection.
935  */
936 static void
937 clear_selection (Atom selection)
938 {
939   XSetSelectionOwner (display, selection, None, timestamp);
940   /* Call XSync to ensure this operation completes before program
941    * termination, especially if this is all we are doing. */
942   XSync (display, False);
943 }
944
945 /*
946  * own_selection (selection)
947  *
948  * Requests ownership of the X selection. Returns True if ownership was
949  * granted, and False otherwise.
950  */
951 static Bool
952 own_selection (Atom selection)
953 {
954   Window owner;
955
956   XSetSelectionOwner (display, selection, window, timestamp);
957   /* XGetSelectionOwner does a round trip to the X server, so there is
958    * no need to call XSync here. */
959   owner = XGetSelectionOwner (display, selection);
960   if (owner != window) {
961     return False;
962   } else {
963     XSetErrorHandler (handle_x_errors);
964     return True;
965   }
966 }
967
968
969 static IncrTrack * incrtrack_list = NULL;
970
971 /*
972  * add_incrtrack (it)
973  *
974  * Add 'it' to the head of incrtrack_list.
975  */
976 static void
977 add_incrtrack (IncrTrack * it)
978 {
979   if (incrtrack_list) {
980     incrtrack_list->prev = it;
981   }
982   it->prev = NULL;
983   it->next = incrtrack_list;
984   incrtrack_list = it;
985 }
986
987 /*
988  * remove_incrtrack (it)
989  *
990  * Remove 'it' from incrtrack_list.
991  */
992 static void
993 remove_incrtrack (IncrTrack * it)
994 {
995   if (it->prev) {
996     it->prev->next = it->next;
997   }
998   if (it->next) {
999     it->next->prev = it->prev;
1000   }
1001
1002   if (incrtrack_list == it) {
1003     incrtrack_list = it->next;
1004   }
1005 }
1006
1007 /*
1008  * fresh_incrtrack ()
1009  *
1010  * Create a new incrtrack, and add it to incrtrack_list.
1011  */
1012 static IncrTrack *
1013 fresh_incrtrack (void)
1014 {
1015   IncrTrack * it;
1016
1017   it = xs_malloc (sizeof (IncrTrack));
1018   add_incrtrack (it);
1019
1020   return it;
1021 }
1022
1023 /*
1024  * trash_incrtrack (it)
1025  *
1026  * Remove 'it' from incrtrack_list, and free it.
1027  */
1028 static void
1029 trash_incrtrack (IncrTrack * it)
1030 {
1031   remove_incrtrack (it);
1032   free (it);
1033 }
1034
1035 /*
1036  * find_incrtrack (atom)
1037  *
1038  * Find the IncrTrack structure within incrtrack_list pertaining to 'atom',
1039  * if it exists.
1040  */
1041 static IncrTrack *
1042 find_incrtrack (Atom atom)
1043 {
1044   IncrTrack * iti;
1045
1046   for (iti = incrtrack_list; iti; iti = iti->next) {
1047     if (atom == iti->property) return iti;
1048   }
1049
1050   return NULL;
1051 }
1052
1053 /* Forward declaration of handle_multiple() */
1054 static HandleResult
1055 handle_multiple (Display * display, Window requestor, Atom property,
1056                  unsigned char * sel, Atom selection, Time time,
1057                  MultTrack * mparent);
1058
1059 /* Forward declaration of process_multiple() */
1060 static HandleResult
1061 process_multiple (MultTrack * mt, Bool do_parent);
1062
1063 /*
1064  * confirm_incr (it)
1065  *
1066  * Confirm the selection request of ITER tracked by 'it'.
1067  */
1068 static void
1069 notify_incr (IncrTrack * it, HandleResult hr)
1070 {
1071   XSelectionEvent ev;
1072
1073   /* Call XSync here to make sure any BadAlloc errors are caught before
1074    * confirming the conversion. */
1075   XSync (it->display, False);
1076
1077   print_debug (D_TRACE, "Confirming conversion");
1078
1079   /* Prepare a SelectionNotify event to send, placing the selection in the
1080    * requested property. */
1081   ev.type = SelectionNotify;
1082   ev.display = it->display;
1083   ev.requestor = it->requestor;
1084   ev.selection = it->selection;
1085   ev.time = it->time;
1086   ev.target = it->target;
1087
1088   if (hr & HANDLE_ERR) ev.property = None;
1089   else ev.property = it->property;
1090
1091   XSendEvent (display, ev.requestor, False,
1092               (unsigned long)NULL, (XEvent *)&ev);
1093 }
1094
1095 /*
1096  * refuse_all_incr ()
1097  *
1098  * Refuse all INCR transfers in progress. ASSUMES that this is called in
1099  * response to an error, and that the program is about to bail out;
1100  * ie. incr_track is not cleaned out.
1101  */
1102 static void
1103 refuse_all_incr (void)
1104 {
1105   IncrTrack * it;
1106
1107   for (it = incrtrack_list; it; it = it->next) {
1108     XDeleteProperty (it->display, it->requestor, it->property);
1109     notify_incr (it, HANDLE_ERR);
1110     /* Don't bother trashing and list-removing these; we are about to
1111      * bail out anyway. */
1112   }
1113 }
1114
1115 /*
1116  * complete_incr (it)
1117  *
1118  * Finish off an INCR retrieval. If it was part of a multiple, continue
1119  * that; otherwise, send confirmation that this completed.
1120  */
1121 static void
1122 complete_incr (IncrTrack * it, HandleResult hr)
1123 {
1124   MultTrack * mparent = it->mparent;
1125
1126   if (mparent) {
1127     trash_incrtrack (it);
1128     process_multiple (mparent, True);
1129   } else {
1130     notify_incr (it, hr);
1131     trash_incrtrack (it);
1132   }
1133 }
1134
1135 /*
1136  * notify_multiple (mt, hr)
1137  *
1138  * Confirm the selection request initiated with MULTIPLE tracked by 'mt'.
1139  */
1140 static void
1141 notify_multiple (MultTrack * mt, HandleResult hr)
1142 {
1143   XSelectionEvent ev;
1144
1145   /* Call XSync here to make sure any BadAlloc errors are caught before
1146    * confirming the conversion. */
1147   XSync (mt->display, False);
1148
1149   /* Prepare a SelectionNotify event to send, placing the selection in the
1150    * requested property. */
1151   ev.type = SelectionNotify;
1152   ev.display = mt->display;
1153   ev.requestor = mt->requestor;
1154   ev.selection = mt->selection;
1155   ev.time = mt->time;
1156   ev.target = multiple_atom;
1157
1158   if (hr & HANDLE_ERR) ev.property = None;
1159   else ev.property = mt->property;
1160
1161   XSendEvent (display, ev.requestor, False,
1162               (unsigned long)NULL, (XEvent *)&ev);
1163 }
1164
1165 /*
1166  * complete_multiple (mt, do_parent, hr)
1167  *
1168  * Complete a MULTIPLE transfer. Iterate to its parent MULTIPLE if
1169  * 'do_parent' is true. If there is not parent MULTIPLE, send notification
1170  * of its completion with status 'hr'.
1171  */
1172 static void
1173 complete_multiple (MultTrack * mt, Bool do_parent, HandleResult hr)
1174 {
1175   MultTrack * mparent = mt->mparent;
1176
1177   if (mparent) {
1178     free (mt);
1179     if (do_parent) process_multiple (mparent, True);
1180   } else {
1181     notify_multiple (mt, hr);
1182     free (mt);
1183   }
1184 }
1185
1186 /*
1187  * change_property (display, requestor, property, target, format, mode,
1188  *                  data, nelements)
1189  *
1190  * Wrapper to XChangeProperty that performs INCR transfer if required and
1191  * returns status of entire transfer.
1192  */
1193 static HandleResult
1194 change_property (Display * display, Window requestor, Atom property,
1195                  Atom target, int format, int mode,
1196                  unsigned char * data, int nelements,
1197                  Atom selection, Time time, MultTrack * mparent)
1198 {
1199   XSelectionEvent ev;
1200   int nr_bytes;
1201   IncrTrack * it;
1202
1203   print_debug (D_TRACE, "change_property ()");
1204
1205   nr_bytes = nelements * format / 8;
1206
1207   if (nr_bytes <= max_req) {
1208     print_debug (D_TRACE, "data within maximum request size");
1209     XChangeProperty (display, requestor, property, target, format, mode,
1210                      data, nelements);
1211
1212     return HANDLE_OK;
1213   }
1214
1215   /* else */
1216   print_debug (D_TRACE, "large data transfer");
1217
1218
1219   /* Send a SelectionNotify event of type INCR */
1220   ev.type = SelectionNotify;
1221   ev.display = display;
1222   ev.requestor = requestor;
1223   ev.selection = selection;
1224   ev.time = time;
1225   ev.target = incr_atom; /* INCR */
1226   ev.property = property;
1227
1228   XSelectInput (ev.display, ev.requestor, PropertyChangeMask);
1229
1230   XChangeProperty (ev.display, ev.requestor, ev.property, incr_atom, 32,
1231                    PropModeReplace, (unsigned char *)&nr_bytes, 1);
1232
1233   XSendEvent (display, requestor, False,
1234               (unsigned long)NULL, (XEvent *)&ev);
1235
1236   /* Set up the IncrTrack to track this */
1237   it = fresh_incrtrack ();
1238
1239   it->mparent = mparent;
1240   it->state = S_INCR_1;
1241   it->display = display;
1242   it->requestor = requestor;
1243   it->property = property;
1244   it->selection = selection;
1245   it->time = time;
1246   it->target = target;
1247   it->format = format;
1248   it->data = data;
1249   it->nelements = nelements;
1250   it->offset = 0;
1251
1252   /* Maximum nr. of elements that can be transferred in one go */
1253   it->max_elements = max_req * 8 / format;
1254
1255   /* Nr. of elements to transfer in this instance */
1256   it->chunk = MIN (it->max_elements, it->nelements - it->offset);
1257
1258   /* Wait for that property to get deleted */
1259   print_debug (D_TRACE, "Waiting on intial property deletion (%s)",
1260                get_atom_name (it->property));
1261
1262   return HANDLE_INCOMPLETE;
1263 }
1264
1265 static HandleResult
1266 incr_stage_1 (IncrTrack * it)
1267 {
1268   /* First pass: PropModeReplace, from data, size chunk */
1269   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);
1270   XChangeProperty (it->display, it->requestor, it->property, it->target,
1271                    it->format, PropModeReplace, it->data, it->chunk);
1272
1273   it->offset += it->chunk;
1274
1275   /* wait for PropertyNotify events */
1276   print_debug (D_TRACE, "Waiting on subsequent deletions ...");
1277
1278   it->state = S_INCR_2;
1279
1280   return HANDLE_INCOMPLETE;
1281 }
1282
1283 static HandleResult
1284 incr_stage_2 (IncrTrack * it)
1285 {
1286   it->chunk = MIN (it->max_elements, it->nelements - it->offset);
1287
1288   if (it->chunk <= 0) {
1289
1290     /* Now write zero-length data to the property */
1291     XChangeProperty (it->display, it->requestor, it->property, it->target,
1292                      it->format, PropModeAppend, NULL, 0);
1293     it->state = S_NULL;
1294     print_debug (D_TRACE, "Set si to state S_NULL");
1295     return HANDLE_OK;
1296   } else {
1297     print_debug (D_TRACE, "Writing chunk (%d bytes) to property",
1298                  it->chunk);
1299     XChangeProperty (it->display, it->requestor, it->property, it->target,
1300                      it->format, PropModeAppend, it->data+it->offset,
1301                      it->chunk);
1302     it->offset += it->chunk;
1303     print_debug (D_TRACE, "%d bytes remaining",
1304                  it->nelements - it->offset);
1305     return HANDLE_INCOMPLETE;
1306   }
1307 }
1308
1309
1310 /*
1311  * handle_timestamp (display, requestor, property)
1312  *
1313  * Handle a TIMESTAMP request.
1314  */
1315 static HandleResult