summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsabetts <sabetts>2004-12-13 01:04:15 +0000
committersabetts <sabetts>2004-12-13 01:04:15 +0000
commitf90ee8e9dfcb3ab286c5202e96a0715429f0e4e7 (patch)
tree43fd7965bcae0bf813f9609c4bf815cb6df09aff
parentf6f24bd90f416993e1621266081bfc9419925495 (diff)
downloadratpoison-f90ee8e9dfcb3ab286c5202e96a0715429f0e4e7.zip
* src/sbuf.c (sbuf_concat): call sbuf_nconcat. move bulk of body
to sbuf_nconcat. (sbuf_nconcat): new function * src/globals.c (init_globals): new function * src/main.c (main): call init_globals. * src/globals.c: include unistd.h. (x_export_selection): new function (set_nselection): likewise (set_selection): call x_export_selection (get_cut_buffer): new function (get_primary_selection): likewise (get_selection): likewise * src/editor.c (editor_kill_word): add the deleted text to the X11 selection (editor_backward_kill_word): likewise (editor_kill_line): likewise (backward_kill_line): new fuction (editor_backward_kill_line): add the deleted text to the X11 selection. call backward_kill_line. (paste_cut_buffer): remove function (paste_primary_selection): likewise (editor_paste_selection): call get_selection to get the X11 selection. (editor_complete): call backward_kill_line instead of editor_backward_kill_line. * src/ratpoison.h: Include string.h. Include X11/XAtom.h. * src/globals.c (selection): new global (set_selection): new function * src/events.c: include X11/Xmd.h (selection_request): new function (selection_clear): new function (delegate_event): call selection_request and selection_clear for SelectionRequest and SelectionClear events. * src/actions.c (user_commands): remove duplicate focusprev entry. Add putsel. Add getsel. (cmd_putsel): new function (cmd_getsel): new function * contrib/genrpbindings: add a missing paren to the elisp bindings.
-rw-r--r--ChangeLog51
-rw-r--r--NEWS4
-rwxr-xr-xcontrib/genrpbindings2
-rw-r--r--doc/ratpoison.texi8
-rw-r--r--src/actions.c23
-rw-r--r--src/actions.h3
-rw-r--r--src/data.h6
-rw-r--r--src/editor.c146
-rw-r--r--src/events.c81
-rw-r--r--src/globals.c146
-rw-r--r--src/globals.h9
-rw-r--r--src/main.c1
-rw-r--r--src/ratpoison.h2
-rw-r--r--src/sbuf.c11
-rw-r--r--src/sbuf.h1
15 files changed, 374 insertions, 120 deletions
diff --git a/ChangeLog b/ChangeLog
index 4b41302..5802c41 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,57 @@
+2004-12-12 Shawn Betts <sabetts@vcn.bc.ca>
+
+ * src/sbuf.c (sbuf_concat): call sbuf_nconcat. move bulk of body
+ to sbuf_nconcat.
+ (sbuf_nconcat): new function
+
+ * src/globals.c (init_globals): new function
+
+ * src/main.c (main): call init_globals.
+
+ * src/globals.c: include unistd.h.
+ (x_export_selection): new function
+ (set_nselection): likewise
+ (set_selection): call x_export_selection
+ (get_cut_buffer): new function
+ (get_primary_selection): likewise
+ (get_selection): likewise
+
+ * src/editor.c (editor_kill_word): add the deleted text to the X11
+ selection
+ (editor_backward_kill_word): likewise
+ (editor_kill_line): likewise
+ (backward_kill_line): new fuction
+ (editor_backward_kill_line): add the deleted text to the X11
+ selection. call backward_kill_line.
+ (paste_cut_buffer): remove function
+ (paste_primary_selection): likewise
+ (editor_paste_selection): call get_selection to get the X11
+ selection.
+ (editor_complete): call backward_kill_line instead of
+ editor_backward_kill_line.
+
+ * src/ratpoison.h: Include string.h. Include X11/XAtom.h.
+
+ * src/globals.c (selection): new global
+ (set_selection): new function
+
+ * src/events.c: include X11/Xmd.h
+ (selection_request): new function
+ (selection_clear): new function
+ (delegate_event): call selection_request and selection_clear for
+ SelectionRequest and SelectionClear events.
+
+ * src/actions.c (user_commands): remove duplicate focusprev entry. Add putsel. Add getsel.
+ (cmd_putsel): new function
+ (cmd_getsel): new function
+
+ * contrib/genrpbindings: add a missing paren to the elisp bindings.
+
2004-12-12 Ryan Yeske <rcyeske@gmail.com>
* MERCHANDISE: Add file.
-2004-12-09 Shawn Betts <katia_dilkina@verizon.net>
+2004-12-09 Shawn Betts <sabetts@vcn.bc.ca>
* src/actions.c (cmd_unmanage): fix message formatting
(cmd_ratwarp): likewise
diff --git a/NEWS b/NEWS
index 21f483d..486b273 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,10 @@ Rather than error out, now, ratpoison will just be built without history.
inext, iprev, iother, cnext, cprev, cother, prompt, dedicate,
describekey, focusprev, sdump, sfdump.
+** yet two more new commands: putsel, getsel
+putsel puts the specified text into the X11 selection so other clients
+can yank it. getsel returns the current selection.
+
* Changes since 1.3.0-rc2-beta2
** new format option %f
This options displays the frame number the window is in or a space if
diff --git a/contrib/genrpbindings b/contrib/genrpbindings
index 2325e9e..6c1733b 100755
--- a/contrib/genrpbindings
+++ b/contrib/genrpbindings
@@ -61,7 +61,7 @@ print ELISP <<PREAMBLE;
(defmacro defun-ratpoison (cmd)
`(progn (defun ,(intern (concat "ratpoison-" (symbol-name cmd))) (&rest args)
- (apply 'ratpoison-cmd ,(symbol-name cmd) args)))
+ (apply 'ratpoison-cmd ,(symbol-name cmd) args))))
(defun ratpoison-cmd (cmd &rest args)
(with-temp-buffer
diff --git a/doc/ratpoison.texi b/doc/ratpoison.texi
index 0266045..744ed90 100644
--- a/doc/ratpoison.texi
+++ b/doc/ratpoison.texi
@@ -1289,6 +1289,10 @@ Execute a shell command. By default, @kbd{C-t !} does this.
Display the value of the environment variable, @var{env}.
@end deffn
+@deffn Command getsel
+Return the contents of the X11 selection.
+@end deffn
+
@deffn Command help
Display a help screen that lists all bound keystrokes.
@end deffn
@@ -1301,6 +1305,10 @@ Display ratpoison's license. By default, this is bound to @kbd{C-t V}.
Send a @kbd{C-t} to the current window.
@end deffn
+@deffn Command putsel @var{text}
+Make text the X11 selection.
+@end deffn
+
@deffn Command quit
Quit ratpoison.
@end deffn
diff --git a/src/actions.c b/src/actions.c
index 92e16c4..acda13e 100644
--- a/src/actions.c
+++ b/src/actions.c
@@ -125,14 +125,15 @@ static user_command user_commands[] =
{"cprev", cmd_cprev, arg_VOID},
{"dedicate", cmd_dedicate, arg_VOID},
{"describekey", cmd_describekey, arg_STRING},
- {"focusprev", cmd_prev_frame, arg_VOID},
{"inext", cmd_inext, arg_VOID},
{"iother", cmd_iother, arg_VOID},
{"iprev", cmd_iprev, arg_VOID},
- {"prompt", cmd_prompt, arg_STRING},
+ {"prompt", cmd_prompt, arg_STRING},
{"sdump", cmd_sdump, arg_VOID},
{"sfdump", cmd_sfdump, arg_VOID},
{"undo", cmd_undo, arg_STRING},
+ {"putsel", cmd_putsel, arg_STRING},
+ {"getsel", cmd_getsel, arg_STRING},
/*@end (tag required for genrpbindings) */
/* Commands to help debug ratpoison. */
@@ -4973,3 +4974,21 @@ cmd_dedicate (int interactive, char *data)
return NULL;
}
+char *
+cmd_putsel (int interactive, char *data)
+{
+ if (data == NULL)
+ {
+ message ("putsel: one argument required");
+ return NULL;
+ }
+
+ set_selection(data);
+ return NULL;
+}
+
+char *
+cmd_getsel (int interactive, char *data)
+{
+ return get_selection();
+}
diff --git a/src/actions.h b/src/actions.h
index 028edf1..e046aa9 100644
--- a/src/actions.h
+++ b/src/actions.h
@@ -141,9 +141,10 @@ char *cmd_prompt (int interactive, char *data);
char *cmd_sdump (int interactive, char *data);
char *cmd_sfdump (int interactively, char *data);
char *cmd_undo (int interactive, char *data);
+char *cmd_putsel (int interactive, char *data);
+char *cmd_getsel (int interactive, char *data);
void pop_frame_undo (rp_frame_undo *u);
-
rp_keymap *find_keymap (char *name);
void initialize_default_keybindings (void);
void keymap_free (rp_keymap *map);
diff --git a/src/data.h b/src/data.h
index 81a3edb..7ebb72b 100644
--- a/src/data.h
+++ b/src/data.h
@@ -340,5 +340,11 @@ struct rp_frame_undo
struct list_head node;
};
+typedef struct rp_xselection rp_xselection;
+struct rp_xselection
+{
+ unsigned char *text;
+ int len;
+};
#endif /* _RATPOISON_DATA_H */
diff --git a/src/editor.c b/src/editor.c
index 3a36291..6f312e0 100644
--- a/src/editor.c
+++ b/src/editor.c
@@ -257,6 +257,7 @@ static edit_status
editor_kill_word (rp_input_line *line)
{
int i, diff;
+
if (line->position < line->length)
{
@@ -265,6 +266,9 @@ editor_kill_word (rp_input_line *line)
diff = i - line->position;
+ /* Add the word to the X11 selection. */
+ set_nselection (&line->buffer[line->position], diff);
+
for (i = line->position; i <= line->length - diff; i++)
line->buffer[i] = line->buffer[i + diff];
@@ -288,6 +292,9 @@ editor_backward_kill_word (rp_input_line *line)
line->position = i;
+ /* Add the word to the X11 selection. */
+ set_nselection (&line->buffer[line->position], diff);
+
for (; i <= line->length - diff; i++)
line->buffer[i] = line->buffer[i + diff];
@@ -302,6 +309,9 @@ editor_kill_line (rp_input_line *line)
{
if (line->position < line->length)
{
+ /* Add the line to the X11 selection. */
+ set_selection (&line->buffer[line->position]);
+
line->length = line->position;
line->buffer[line->length] = 0;
}
@@ -309,19 +319,33 @@ editor_kill_line (rp_input_line *line)
return EDIT_DELETE;
}
-static edit_status
-editor_backward_kill_line (rp_input_line *line)
+/* Do the dirty work of killing a line backwards. */
+static void
+backward_kill_line (rp_input_line *line)
{
int i;
+ /* If we're at the beginning, we have nothing to do. */
if (line->position <= 0)
- return EDIT_NO_OP;
+ return;
for (i = line->position; i<= line->length; i++)
line->buffer[i - line->position] = line->buffer[i];
line->length -= line->position;
line->position = 0;
+}
+
+static edit_status
+editor_backward_kill_line (rp_input_line *line)
+{
+ if (line->position <= 0)
+ return EDIT_NO_OP;
+
+ /* Add the line to the X11 selection. */
+ set_nselection (line->buffer, line->position);
+
+ backward_kill_line (line);
return EDIT_DELETE;
}
@@ -469,118 +493,18 @@ editor_enter (rp_input_line *line)
}
static edit_status
-paste_cut_buffer (rp_input_line *line)
-{
- int nbytes;
- char *data;
- edit_status status;
-
- PRINT_DEBUG (("trying the cut buffer\n"));
-
- data = XFetchBytes (dpy, &nbytes);
-
- if (data)
- {
-/* status = editor_insert (line, data, nbytes); */
- status = editor_insert (line, data);
- XFree (data);
- }
- else
- {
- status = EDIT_NO_OP;
- }
-
- return status;
-}
-
-static edit_status
-paste_primary_selection (rp_input_line *line)
-{
- Atom actual_type;
- rp_screen *s = current_screen ();
- int actual_format;
- unsigned long nitems;
- unsigned long offset;
- unsigned long bytes_after;
- unsigned char *data;
-
- if (XGetWindowProperty (dpy, s->input_window, rp_selection, 0, 0, False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data) == Success)
- {
- if (data)
- {
- XFree (data);
- data = NULL;
- }
-
- PRINT_DEBUG (("actual_type = %ld, actual_format = %d, bytes_after = %ld\n", actual_type, actual_format, bytes_after));
-
- if (actual_type != XA_STRING || actual_format != 8)
- {
- PRINT_DEBUG (("selection data is invalid\n"));
- if (data)
- XFree (data);
- return EDIT_NO_OP;
- }
-
- offset = 0;
-
- while (bytes_after > 0)
- {
- if (XGetWindowProperty (dpy, s->input_window, rp_selection, offset / 4, bytes_after / 4 + 1, False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data) != Success)
- break;
-
- PRINT_DEBUG (("bytes_after = %ld, nitems = %ld, data = '%s'\n", bytes_after, nitems, data));
-
- nitems *= actual_format / 8;
- offset += nitems;
-
-/* editor_insert (line, data, nitems); */
- editor_insert (line, (char *)data);
-
- PRINT_DEBUG (("bytes_after = %ld, nitems = %ld, data = '%s'\n", bytes_after, nitems, data));
-
- XFree (data);
- }
- }
-
- /* notify the owner that the data has been transferred */
- XDeleteProperty(dpy, s->input_window, rp_selection);
-
- return EDIT_INSERT;
-}
-
-static edit_status
editor_paste_selection (rp_input_line *line)
{
- Atom property;
- XEvent ev;
- rp_screen *s = current_screen ();
- int loops = 1000;
-
- /* be a good icccm citizen */
- XDeleteProperty (dpy, s->input_window, rp_selection);
- /* TODO: we shouldn't use CurrentTime here, use the time of the XKeyEvent, should we fake it? */
- XConvertSelection (dpy, XA_PRIMARY, XA_STRING, rp_selection, s->input_window, CurrentTime);
-
- while (!XCheckTypedWindowEvent (dpy, s->input_window, SelectionNotify, &ev))
+ char *text;
+ text = get_selection();
+ if (text)
{
- if (loops == 0)
- {
- PRINT_ERROR (("selection request timed out\n"));
- return EDIT_NO_OP;
- }
- usleep (10000);
- loops--;
+ editor_insert(line, text);
+ free (text);
+ return EDIT_INSERT;
}
-
- PRINT_DEBUG (("SelectionNotify event\n"));
-
- property = ev.xselection.property;
-
- if (property != None)
- return paste_primary_selection (line);
else
- return paste_cut_buffer (line);
+ return EDIT_NO_OP;
}
static edit_status
@@ -604,7 +528,7 @@ editor_complete (rp_input_line *line, int direction)
return EDIT_NO_OP;
/* Insert the completion. */
- editor_backward_kill_line (line);
+ backward_kill_line (line);
editor_insert (line, s);
return EDIT_COMPLETE;
diff --git a/src/events.c b/src/events.c
index 00ebb0a..ce11485 100644
--- a/src/events.c
+++ b/src/events.c
@@ -24,6 +24,7 @@
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
+#include <X11/Xmd.h> /* for CARD32. */
#include <stdio.h>
#include <stdlib.h>
@@ -674,6 +675,76 @@ mapping_notify (XMappingEvent *ev)
}
}
+/* This is called whan an application has requested the
+ selection. Copied from rxvt. */
+static void
+selection_request (XSelectionRequestEvent *rq)
+{
+ XEvent ev;
+ CARD32 target_list[4];
+ Atom target;
+ static Atom xa_targets = None;
+ static Atom xa_compound_text = None;
+ static Atom xa_text = None;
+ XTextProperty ct;
+ XICCEncodingStyle style;
+ char *cl[4];
+
+ if (xa_text == None)
+ xa_text = XInternAtom(dpy, "TEXT", False);
+ if (xa_compound_text == None)
+ xa_compound_text = XInternAtom(dpy, "COMPOUND_TEXT", False);
+ if (xa_targets == None)
+ xa_targets = XInternAtom(dpy, "TARGETS", False);
+
+ ev.xselection.type = SelectionNotify;
+ ev.xselection.property = None;
+ ev.xselection.display = rq->display;
+ ev.xselection.requestor = rq->requestor;
+ ev.xselection.selection = rq->selection;
+ ev.xselection.target = rq->target;
+ ev.xselection.time = rq->time;
+
+ if (rq->target == xa_targets) {
+ target_list[0] = (CARD32) xa_targets;
+ target_list[1] = (CARD32) XA_STRING;
+ target_list[2] = (CARD32) xa_text;
+ target_list[3] = (CARD32) xa_compound_text;
+ XChangeProperty(dpy, rq->requestor, rq->property, rq->target,
+ (8 * sizeof(target_list[0])), PropModeReplace,
+ (unsigned char *)target_list,
+ (sizeof(target_list) / sizeof(target_list[0])));
+ ev.xselection.property = rq->property;
+ } else if (rq->target == XA_STRING
+ || rq->target == xa_compound_text
+ || rq->target == xa_text) {
+ if (rq->target == XA_STRING) {
+ style = XStringStyle;
+ target = XA_STRING;
+ } else {
+ target = xa_compound_text;
+ style = (rq->target == xa_compound_text) ? XCompoundTextStyle
+ : XStdICCTextStyle;
+ }
+ cl[0] = selection.text;
+ XmbTextListToTextProperty(dpy, cl, 1, style, &ct);
+ XChangeProperty(dpy, rq->requestor, rq->property,
+ target, 8, PropModeReplace,
+ ct.value, ct.nitems);
+ ev.xselection.property = rq->property;
+ }
+ XSendEvent(dpy, rq->requestor, False, 0, &ev);
+}
+
+static void
+selection_clear ()
+{
+ if (selection.text)
+ free (selection.text);
+ selection.text = NULL;
+ selection.len = 0;
+}
+
/* Given an event, call the correct function to handle it. */
static void
delegate_event (XEvent *ev)
@@ -740,6 +811,14 @@ delegate_event (XEvent *ev)
mapping_notify( &ev->xmapping );
break;
+ case SelectionRequest:
+ selection_request(&ev->xselectionrequest);
+ break;
+
+ case SelectionClear:
+ selection_clear();
+ break;
+
case ConfigureNotify:
case MapNotify:
case Expose:
@@ -747,9 +826,7 @@ delegate_event (XEvent *ev)
case KeyRelease:
case ReparentNotify:
case EnterNotify:
- case SelectionRequest:
case SelectionNotify:
- case SelectionClear:
case CirculateRequest:
/* Ignore these events. */
break;
diff --git a/src/globals.c b/src/globals.c
index bc369ae..a76f220 100644
--- a/src/globals.c
+++ b/src/globals.c
@@ -20,6 +20,8 @@
#include "ratpoison.h"
+#include <unistd.h> /* for usleep(). */
+
int alarm_signalled = 0;
int kill_signalled = 0;
int hup_signalled = 0;
@@ -70,6 +72,143 @@ char *rp_error_msg = NULL;
/* Global frame numset */
struct numset *rp_frame_numset;
+/* The X11 selection globals */
+rp_xselection selection;
+
+static void
+x_export_selection ()
+{
+ /* Hang the selections off screen 0's key window. */
+ XSetSelectionOwner(dpy, XA_PRIMARY, screens[0].key_window, CurrentTime);
+ if (XGetSelectionOwner(dpy, XA_PRIMARY) != screens[0].key_window)
+ PRINT_ERROR(("can't get primary selection"));
+ XChangeProperty(dpy, screens[0].root, XA_CUT_BUFFER0, XA_STRING, 8,
+ PropModeReplace, selection.text, selection.len);
+}
+
+void
+set_nselection (char *txt, int len)
+{
+ int i;
+
+ /* Update the selection structure */
+ if (selection.text != NULL)
+ free(selection.text);
+
+ /* Copy the string by hand. */
+ selection.text = malloc(len+1);
+ selection.len = len + 1;
+ for (i=0; i<len; i++)
+ selection.text[i] = txt[i];
+ selection.text[len] = 0;
+
+ x_export_selection();
+}
+
+void
+set_selection (char *txt)
+{
+ /* Update the selection structure */
+ if (selection.text != NULL)
+ free(selection.text);
+ selection.text = xstrdup (txt);
+ selection.len = strlen (txt);
+
+ x_export_selection();
+}
+
+static char *
+get_cut_buffer ()
+{
+ int nbytes;
+ char *data;
+
+ PRINT_DEBUG (("trying the cut buffer\n"));
+
+ data = XFetchBytes (dpy, &nbytes);
+
+ if (data)
+ {
+ struct sbuf *s = sbuf_new (0);
+ sbuf_nconcat (s, data, nbytes);
+ XFree (data);
+ return sbuf_free_struct (s);
+ }
+ else
+ return NULL;
+}
+
+/* Lifted the code from rxvt. */
+static char *
+get_primary_selection()
+{
+ long nread;
+ unsigned long bytes_after;
+ XTextProperty ct;
+ struct sbuf *s = sbuf_new(0);
+
+ for (nread = 0, bytes_after = 1; bytes_after > 0; nread += ct.nitems) {
+ if ((XGetWindowProperty(dpy, current_screen()->input_window, rp_selection, (nread / 4), 4096,
+ True, AnyPropertyType, &ct.encoding,
+ &ct.format, &ct.nitems, &bytes_after,
+ &ct.value) != Success)) {
+ XFree(ct.value);
+ sbuf_free(s);
+ return NULL;
+ }
+ if (ct.value == NULL)
+ continue;
+ /* Accumulate the data. FIXME: ct.value may not be NULL
+ terminated. */
+ sbuf_nconcat (s, ct.value, ct.nitems);
+ XFree(ct.value);
+ }
+ return sbuf_free_struct (s);
+}
+
+char *
+get_selection ()
+{
+ Atom property;
+ XEvent ev;
+ rp_screen *s = current_screen ();
+ int loops = 1000;
+
+ /* Just insert our text, if we own the selection. */
+ if (selection.text)
+ {
+ return xstrdup (selection.text);
+ }
+ else
+ {
+ /* be a good icccm citizen */
+ XDeleteProperty (dpy, s->input_window, rp_selection);
+ /* TODO: we shouldn't use CurrentTime here, use the time of the XKeyEvent, should we fake it? */
+ XConvertSelection (dpy, XA_PRIMARY, XA_STRING, rp_selection, s->input_window, CurrentTime);
+
+ /* This seems like a hack. */
+ while (!XCheckTypedWindowEvent (dpy, s->input_window, SelectionNotify, &ev))
+ {
+ if (loops == 0)
+ {
+ PRINT_ERROR (("selection request timed out\n"));
+ return NULL;
+ }
+ usleep (10000);
+ loops--;
+ }
+
+ PRINT_DEBUG (("SelectionNotify event\n"));
+
+ property = ev.xselection.property;
+
+ if (property != None)
+ return get_primary_selection ();
+ else
+ return get_cut_buffer ();
+ }
+}
+
/* The hook dictionary globals. */
LIST_HEAD (rp_key_hook);
@@ -106,3 +245,10 @@ set_window_focus (Window window)
LIST_HEAD (rp_frame_undos);
int rp_num_frame_undos = 0;
+
+void
+init_globals ()
+{
+ selection.text = NULL;
+ selection.len = 0;
+}
diff --git a/src/globals.h b/src/globals.h
index 45cb54c..8adb6ff 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -148,4 +148,13 @@ extern struct numset *rp_frame_numset;
extern struct list_head rp_frame_undos;
extern int rp_num_frame_undos;
+
+/* Selection handling globals */
+extern rp_xselection selection;
+void set_selection (char *txt);
+void set_nselection (char *txt, int len);
+char *get_selection ();
+
+void init_globals ();
+
#endif
diff --git a/src/main.c b/src/main.c
index e75d1d9..acae288 100644
--- a/src/main.c
+++ b/src/main.c
@@ -615,6 +615,7 @@ main (int argc, char *argv[])
set_sig_handler (SIGCHLD, chld_handler);
/* Setup ratpoison's internal structures */
+ init_globals ();
init_defaults ();
init_groups ();
init_window_stuff ();
diff --git a/src/ratpoison.h b/src/ratpoison.h
index 89a8059..1c84bb4 100644
--- a/src/ratpoison.h
+++ b/src/ratpoison.h
@@ -29,7 +29,9 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
+#include <string.h>
#include <X11/Xlib.h>
+#include <X11/Xatom.h>
#include <fcntl.h>
/* Some systems don't define the close-on-exec flag in fcntl.h */
diff --git a/src/sbuf.c b/src/sbuf.c
index 640b283..53c1490 100644
--- a/src/sbuf.c
+++ b/src/sbuf.c
@@ -69,9 +69,9 @@ sbuf_free_struct (struct sbuf *b)
}
char *
-sbuf_concat (struct sbuf *b, const char *str)
+sbuf_nconcat (struct sbuf *b, const char *str, int len)
{
- size_t minsz = b->len + strlen (str) + 1;
+ size_t minsz = b->len + len + 1;
if (b->maxsz < minsz)
{
@@ -86,6 +86,13 @@ sbuf_concat (struct sbuf *b, const char *str)
return b->data;
}
+
+char *
+sbuf_concat (struct sbuf *b, const char *str)
+{
+ return sbuf_nconcat (b, str, strlen (str));
+}
+
char *
sbuf_copy (struct sbuf *b, const char *str)
{
diff --git a/src/sbuf.h b/src/sbuf.h
index 66be1d1..071ab7d 100644
--- a/src/sbuf.h
+++ b/src/sbuf.h
@@ -39,6 +39,7 @@ struct sbuf *sbuf_new (size_t initsz);
void sbuf_free (struct sbuf *b);
char *sbuf_free_struct (struct sbuf *b);
char *sbuf_concat (struct sbuf *b, const char *str);
+char *sbuf_nconcat (struct sbuf *b, const char *str, int len);
char *sbuf_copy (struct sbuf *b, const char *str);
char *sbuf_clear (struct sbuf *b);
char *sbuf_get (struct sbuf *b);