+void *
+xcalloc(size_t nmemb, size_t size) {
+ void *p = calloc(nmemb, size);
+
+ if(!p)
+ die("Out of memory\n");
+
+ return p;
+}
+
+int
+utf8decode(char *s, long *u) {
+ uchar c;
+ int i, n, rtn;
+
+ rtn = 1;
+ c = *s;
+ if(~c & 0x80) { /* 0xxxxxxx */
+ *u = c;
+ return rtn;
+ } else if((c & 0xE0) == 0xC0) { /* 110xxxxx */
+ *u = c & 0x1F;
+ n = 1;
+ } else if((c & 0xF0) == 0xE0) { /* 1110xxxx */
+ *u = c & 0x0F;
+ n = 2;
+ } else if((c & 0xF8) == 0xF0) { /* 11110xxx */
+ *u = c & 0x07;
+ n = 3;
+ } else {
+ goto invalid;
+ }
+
+ for(i = n, ++s; i > 0; --i, ++rtn, ++s) {
+ c = *s;
+ if((c & 0xC0) != 0x80) /* 10xxxxxx */
+ goto invalid;
+ *u <<= 6;
+ *u |= c & 0x3F;
+ }
+
+ if((n == 1 && *u < 0x80) ||
+ (n == 2 && *u < 0x800) ||
+ (n == 3 && *u < 0x10000) ||
+ (*u >= 0xD800 && *u <= 0xDFFF)) {
+ goto invalid;
+ }
+
+ return rtn;
+invalid:
+ *u = 0xFFFD;
+
+ return rtn;
+}
+
+int
+utf8encode(long *u, char *s) {
+ uchar *sp;
+ ulong uc;
+ int i, n;
+
+ sp = (uchar *)s;
+ uc = *u;
+ if(uc < 0x80) {
+ *sp = uc; /* 0xxxxxxx */
+ return 1;
+ } else if(*u < 0x800) {
+ *sp = (uc >> 6) | 0xC0; /* 110xxxxx */
+ n = 1;
+ } else if(uc < 0x10000) {
+ *sp = (uc >> 12) | 0xE0; /* 1110xxxx */
+ n = 2;
+ } else if(uc <= 0x10FFFF) {
+ *sp = (uc >> 18) | 0xF0; /* 11110xxx */
+ n = 3;
+ } else {
+ goto invalid;
+ }
+
+ for(i=n,++sp; i>0; --i,++sp)
+ *sp = ((uc >> 6*(i-1)) & 0x3F) | 0x80; /* 10xxxxxx */
+
+ return n+1;
+invalid:
+ /* U+FFFD */
+ *s++ = '\xEF';
+ *s++ = '\xBF';
+ *s = '\xBD';
+
+ return 3;
+}
+
+/* use this if your buffer is less than UTF_SIZ, it returns 1 if you can decode
+ UTF-8 otherwise return 0 */
+int
+isfullutf8(char *s, int b) {
+ uchar *c1, *c2, *c3;
+
+ c1 = (uchar *)s;
+ c2 = (uchar *)++s;
+ c3 = (uchar *)++s;
+ if(b < 1) {
+ return 0;
+ } else if((*c1 & 0xE0) == 0xC0 && b == 1) {
+ return 0;
+ } else if((*c1 & 0xF0) == 0xE0 &&
+ ((b == 1) ||
+ ((b == 2) && (*c2 & 0xC0) == 0x80))) {
+ return 0;
+ } else if((*c1 & 0xF8) == 0xF0 &&
+ ((b == 1) ||
+ ((b == 2) && (*c2 & 0xC0) == 0x80) ||
+ ((b == 3) && (*c2 & 0xC0) == 0x80 && (*c3 & 0xC0) == 0x80))) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+int
+utf8size(char *s) {
+ uchar c = *s;
+
+ if(~c & 0x80) {
+ return 1;
+ } else if((c & 0xE0) == 0xC0) {
+ return 2;
+ } else if((c & 0xF0) == 0xE0) {
+ return 3;
+ } else {
+ return 4;
+ }
+}
+
+static void
+selinit(void) {
+ memset(&sel.tclick1, 0, sizeof(sel.tclick1));
+ memset(&sel.tclick2, 0, sizeof(sel.tclick2));
+ sel.mode = 0;
+ sel.ob.x = -1;
+ sel.clip = NULL;
+ sel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
+ if(sel.xtarget == None)
+ sel.xtarget = XA_STRING;
+}
+
+static int
+x2col(int x) {
+ x -= borderpx;
+ x /= xw.cw;
+
+ return LIMIT(x, 0, term.col-1);
+}
+
+static int
+y2row(int y) {
+ y -= borderpx;
+ y /= xw.ch;
+
+ return LIMIT(y, 0, term.row-1);
+}
+
+static void
+selsort(void) {
+ if(sel.ob.y == sel.oe.y) {
+ sel.nb.x = MIN(sel.ob.x, sel.oe.x);
+ sel.ne.x = MAX(sel.ob.x, sel.oe.x);
+ } else {
+ sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
+ sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
+ }
+ sel.nb.y = MIN(sel.ob.y, sel.oe.y);
+ sel.ne.y = MAX(sel.ob.y, sel.oe.y);
+}
+
+static inline bool
+selected(int x, int y) {
+ if(sel.ne.y == y && sel.nb.y == y)
+ return BETWEEN(x, sel.nb.x, sel.ne.x);
+
+ if(sel.type == SEL_RECTANGULAR) {
+ return ((sel.nb.y <= y && y <= sel.ne.y)
+ && (sel.nb.x <= x && x <= sel.ne.x));
+ }
+
+ return ((sel.nb.y < y && y < sel.ne.y)
+ || (y == sel.ne.y && x <= sel.ne.x))
+ || (y == sel.nb.y && x >= sel.nb.x
+ && (x <= sel.ne.x || sel.nb.y != sel.ne.y));
+}
+
+void
+selsnap(int mode, int *x, int *y, int direction) {
+ int i;
+
+ switch(mode) {
+ case SNAP_WORD:
+ /*
+ * Snap around if the word wraps around at the end or
+ * beginning of a line.
+ */
+ for(;;) {
+ if(direction < 0 && *x <= 0) {
+ if(*y > 0 && term.line[*y - 1][term.col-1].mode
+ & ATTR_WRAP) {
+ *y -= 1;
+ *x = term.col-1;
+ } else {
+ break;
+ }
+ }
+ if(direction > 0 && *x >= term.col-1) {
+ if(*y < term.row-1 && term.line[*y][*x].mode
+ & ATTR_WRAP) {
+ *y += 1;
+ *x = 0;
+ } else {
+ break;
+ }
+ }
+
+ if(strchr(worddelimiters,
+ term.line[*y][*x + direction].c[0])) {
+ break;
+ }
+
+ *x += direction;
+ }
+ break;
+ case SNAP_LINE:
+ /*
+ * Snap around if the the previous line or the current one
+ * has set ATTR_WRAP at its end. Then the whole next or
+ * previous line will be selected.
+ */
+ *x = (direction < 0) ? 0 : term.col - 1;
+ if(direction < 0 && *y > 0) {
+ for(; *y > 0; *y += direction) {
+ if(!(term.line[*y-1][term.col-1].mode
+ & ATTR_WRAP)) {
+ break;
+ }
+ }
+ } else if(direction > 0 && *y < term.row-1) {
+ for(; *y < term.row; *y += direction) {
+ if(!(term.line[*y][term.col-1].mode
+ & ATTR_WRAP)) {
+ break;
+ }