// Copyright status: this file is in the public domain. #include #include #include #include #include #include "lx.h" #include "internal.h" // This file knows we always use big-endian LX_U16 r_card16(const unsigned char *pp) { return((pp[0]*256U)+pp[1]); } int r_int16(const unsigned char *pp) { LX_U16 v; v = r_card16(pp); if (v & 0x8000) return((int)((int)(v&0x7fff)-0x8000)); else return(v); } void w_card16(unsigned char *pp, LX_U16 val) { if (val > 65535) lx_abort(); pp[0] = val >> 8; pp[1] = val & 0xff; } // Always BE; because we always run BE, same as w_card16. void w_char2b(unsigned char *pp, unsigned int val) { if (val > 65535) lx_abort(); pp[0] = val >> 8; pp[1] = val & 0xff; } void w_int16(unsigned char *pp, LX_S16 val) { /* * I would normally put a firewall * if ((val < -32768) || (val > 32767)) lx_abort(); * here, but it draws "warning: comparison is always 0 due to limited * range of data type". I'm not sure what to do about that. The * similar tests in w_card16 and w_card32 don't, for reasons I don't * understand, and I like this kind of firewalling. I'm skipping it * for now, to make it build until I figure out what the right thing * to do is. */ pp[0] = (LX_U16)val >> 8; pp[1] = (LX_U16)val & 0xff; } LX_U32 r_card32(const unsigned char *pp) { return((pp[0]*16777216U)+(pp[1]*65536U)+(pp[2]*256U)+pp[3]); } void w_card32(unsigned char *pp, LX_U32 val) { if (val > 0xffffffff) lx_abort(); pp[0] = val >> 24 ; pp[1] = (val >> 16) & 0xff; pp[2] = (val >> 8) & 0xff; pp[3] = val & 0xff; } void w_int32(unsigned char *pp, int val) { pp[0] = (LX_U32)val >> 24; pp[1] = ((LX_U32)val >> 16) & 0xff; pp[2] = ((LX_U32)val >> 8) & 0xff; pp[3] = (LX_U32)val & 0xff; } int r_int32(const unsigned char *pp) { LX_U32 v; v = r_card32(pp); if (v & 0x80000000) return((int)((int)(v&0x7fffffff)-0x80000000)); else return(v); } /* * Always uses big-endian. Because we always run big-endian anyway, * this is identical to w_card32. */ void w_card32be(unsigned char *pp, LX_U32 val) { if (val > 0xffffffff) lx_abort(); pp[0] = val >> 24 ; pp[1] = (val >> 16) & 0xff; pp[2] = (val >> 8) & 0xff; pp[3] = val & 0xff; } static void clear_seqsync(void *xcv) { ((LX_CONN *)xcv)->flags &= ~XCF_SEQSYNC; } /* * The only thing of much note here is the test to see if we're 0x2000 * requests without a response, and the code it controls. This code * is here to ensure that our idea of the sequence number doesn't get * out of sync with the server's. Sequence numbers are 32 bits, but * only the low 16 bits appears on the wire; given the logic * advance_iseq uses for advancing last_iserial, we mustn't let * next_oserial get too far ahead of last_iserial. 0x2000 is very * conservative - by a factor of at least 4, possibly nearly 8 - but * it strikes me as unlikely for any but the most output-heavy clients * to push 8K requests without anything back, and the cost of * resyncing is low. */ static void post_request(LX_CONN *xc) { xc->last_oserial = xc->next_oserial++; if ( ((LX_U32)(xc->next_oserial-xc->last_iserial) > 0x2000) && !(xc->flags & XCF_SEQSYNC) ) { xc->flags |= XCF_SEQSYNC; lx_op_callback(lx_GetInputFocus(xc,0,0),&clear_seqsync,xc,0); } } void lx__send_req(LX_CONN *xc, const unsigned char *req, int len) { if (len < 0) len = r_card16(req+2) * 4; if (aio_oq_queue_copy(&xc->oq,req,len) < 0) { lx__nomem_fail(xc); return; } post_request(xc); } void lx__send_req_free(LX_CONN *xc, unsigned char *req, int len) { if (len < 0) len = r_card16(req+2) * 4; if (aio_oq_queue_free(&xc->oq,req,len) < 0) { lx__nomem_fail(xc); return; } post_request(xc); } void lx__just_send(LX_CONN *xc, const unsigned char *req, int len) { if (aio_oq_queue_copy(&xc->oq,req,len) < 0) lx__nomem_fail(xc); } void lx__send_padding(LX_CONN *xc, unsigned int len) { if ((len & 3) && (aio_oq_queue_point(&xc->oq,"\0\0\0",4-(len&3)) < 0)) lx__nomem_fail(xc); } static void op_link(LX_OP *op) { LX_OP **rootf; LX_OP **rootb; if (op->flags & LXOF_LINK) lx_abort(); rootf = (op->flags & LXOF_DETACH) ? &op->conn->detachf : &op->conn->pendf; rootb = (op->flags & LXOF_DETACH) ? &op->conn->detachb : &op->conn->pendb; op->flink = 0; op->blink = *rootb; if (op->blink) op->blink->flink = op; else *rootf = op; *rootb = op; op->flags |= LXOF_LINK; } static void op_unlink(LX_OP *op) { LX_OP **rootf; LX_OP **rootb; if (! (op->flags & LXOF_LINK)) lx_abort(); rootf = (op->flags & LXOF_DETACH) ? &op->conn->detachf : &op->conn->pendf; rootb = (op->flags & LXOF_DETACH) ? &op->conn->detachb : &op->conn->pendb; if (op->flink) op->flink->blink = op->blink; else *rootb = op->blink; if (op->blink) op->blink->flink = op->flink; else *rootf = op->flink; op->flags &= ~LXOF_LINK; } static void op_free(LX_OP *op) { free(op); } void lx_op_callback(LX_OP *op, void (*cb)(void *), void *arg, unsigned int flags) { if (! op) return; if (op->flags & LXOF_DROP) lx_abort(); if (op->flags & LXOF_DONE) { (*cb)(arg); if (! (flags & LX_OP_KEEP)) lx_op_drop(op); return; } if (flags & LX_OP_KEEP) op->flags |= LXOF_KEEP; else op->flags &= ~LXOF_KEEP; op->usercb = cb; op->usercbarg = arg; } int lx_op_test(LX_OP *op) { if (!op || (op->flags & LXOF_DROP)) lx_abort(); return((op->flags&LXOF_DONE)?1:0); } void lx_op_drop(LX_OP *op) { if (! op) return; if (op->flags & LXOF_DROP) lx_abort(); op->flags = (op->flags & ~LXOF_KEEP) | LXOF_DROP; if (op->flags & LXOF_DONE) { op_unlink(op); op_free(op); } } void lx_op_abort(LX_OP *op) { if (! op) return; if (op->flags & LXOF_DROP) lx_abort(); op->flags = (op->flags & ~LXOF_KEEP) | LXOF_DROP | LXOF_NOWRITE; if (op->flags & LXOF_DONE) { op_unlink(op); op_free(op); } } void lx_op_set_udata(LX_OP *op, void *ud) { if (op->flags & LXOF_DROP) lx_abort(); op->udata = ud; } void *lx_op_udata(LX_OP *op) { if (op->flags & LXOF_DROP) lx_abort(); return(op->udata); } LX_OP_ERR_SETTING lx_op_err(LX_OP *op, int (*fn)(LX_CONN *, LX_OP *, const LX_X_ERR *, void *), void *arg) { LX_OP_ERR_SETTING old; old.fn = op->uerr; old.arg = op->uerrarg; op->uerr = fn; op->uerrarg = arg; return(old); } LX_OP_ERR_SETTING lx_op_err_setting(LX_OP *op, LX_OP_ERR_SETTING new) { LX_OP_ERR_SETTING old; old.fn = op->uerr; old.arg = op->uerrarg; op->uerr = new.fn; op->uerrarg = new.arg; return(old); } void lx__protoerr_v(LX_CONN *xc, const char *fmt, va_list ap) { char *s; int l; LX_LIB_ERR e; l = vasprintf(&s,fmt,ap); if (xc->flags & XCF_OPENING) { if (l < 0) { (*xc->os->err)(&lx__nomem_err,xc->os->cbarg); } else { e.type = LX_LE_PROTO_ERR; e.u.proto_err.msg = s; (*xc->os->err)(&e,xc->os->cbarg); } } else { if (l < 0) { (*xc->lib_err)(xc,&lx__nomem_err); } else { e.type = LX_LE_PROTO_ERR; e.u.proto_err.msg = s; (*xc->lib_err)(xc,&e); } } xc->flags |= XCF_FAIL; } void lx__protoerr(LX_CONN *xc, const char *fmt, ...) { va_list ap; va_start(ap,fmt); lx__protoerr_v(xc,fmt,ap); va_end(ap); } static int op_no_errs( LX_CONN *xc __attribute__((__unused__)), const LX_X_ERR *err __attribute__((__unused__)), void *arg __attribute__((__unused__)) ) { return(0); } static LX_OP *new_lx_op(LX_CONN *c, LX_U32 ser) { LX_OP *op; op = malloc(sizeof(LX_OP)); if (op) { op->conn = c; op->serial = ser; op->usercb = 0; op->usercbarg = 0; op->done = 0; op->err = &op_no_errs; op->uerr = 0; op->reqarg = 0; op->flags = LXOF_KEEP; op->udata = 0; } return(op); } LX_OP *lx__expect_reply( LX_CONN *xc, const unsigned char *req, int reqlen, void (*done)(LX_CONN *, const unsigned char *, void *, unsigned int), void *arg ) { LX_OP *op; LX_U32 ser; ser = xc->next_oserial; lx__send_req(xc,req,reqlen); if (xc->flags & XCF_FAIL) return(0); op = new_lx_op(xc,ser); if (op == 0) { lx__nomem_fail(xc); return(0); } op_link(op); op->done = done; op->reqarg = arg; return(op); } LX_OP *lx_op_predone(LX_CONN *xc) { LX_OP *op; if (xc->flags & XCF_FAIL) return(0); op = new_lx_op(xc,0); if (op == 0) { lx__nomem_fail(xc); return(0); } op->flags |= LXOF_DONE | LXOF_DETACH; op_link(op); return(op); } LX_OP *lx__internal_op(LX_CONN *xc) { LX_OP *op; if (xc->flags & XCF_FAIL) return(0); op = new_lx_op(xc,0); if (op == 0) { lx__nomem_fail(xc); return(0); } op->flags |= LXOF_DETACH; op_link(op); return(op); } static int rb_room(LX_CONN *xc, int n) { if (xc->rba < n) { unsigned char *new; new = realloc(xc->rb,n); if (new) { xc->rb = new; xc->rba = n; return(0); } return(1); } return(0); } void lx__proto_newpkt(LX_CONN *xc) { if (rb_room(xc,32)) { lx__nomem_fail(xc); return; } xc->rbl = 0; xc->rbw = 32; } void lx__dumphex(const char *tag, const unsigned char *data, int len) { int i; if (tag) printf("%s\n",tag); for (i=0;ilast_iserial; if (protoseq > (LX_U16)(xc->last_iserial&0xffff)) { xc->last_iserial = (xc->last_iserial & 0xffff0000) | protoseq; } else if (protoseq < (LX_U16)(xc->last_iserial&0xffff)) { xc->last_iserial = ((xc->last_iserial & 0xffff0000) + 0x10000) | protoseq; } while (xc->pendf && ((LX_U32)(xc->pendf->serial-prev) < (LX_U32)(xc->last_iserial-prev))) { if (! (xc->pendf->flags & LXOF_NOREPLY)) lx__protoerr(xc,"missing reply, sequence %08lx",(unsigned long int)xc->pendf->serial); lx__op_finished(xc->pendf); } } void lx__op_finished(LX_OP *op) { if (op->flags & LXOF_DROP) { op_unlink(op); op_free(op); } else { if (op->usercb) (*op->usercb)(op->usercbarg); op->flags |= LXOF_DONE; op_unlink(op); if (op->flags & LXOF_KEEP) { op->flags |= LXOF_DETACH; op_link(op); } else { op_free(op); } } } // XXX Figure out how to cancel LX_OP for an erroring request! static void process_error(LX_CONN *xc) { LX_X_ERR e; LX_U16 seq; LX_U32 val; LX_U16 minor; LX_U8 major; if (xc->rbl != 32) lx_abort(); seq = r_card16(xc->rb+2); advance_iseq(xc,seq); if (xc->flags & XCF_FAIL) return; val = r_card32(xc->rb+4); minor = r_card16(xc->rb+8); major = xc->rb[10]; #define NOVAL(code,tn,field) \ case code: \ e.type = LX_XE_##tn; \ e.u.field.seq = seq; \ e.u.field.opc_major = major; \ e.u.field.opc_minor = minor; \ break; #define VAL(code,tn,field,valfield) \ case code: \ e.type = LX_XE_##tn; \ e.u.field.seq = seq; \ e.u.field.valfield = val; \ e.u.field.opc_major = major; \ e.u.field.opc_minor = minor; \ break; switch (xc->rb[1]) { NOVAL(1,Request,request) VAL(2,Value,value,value) VAL(3,Window,window,id) VAL(4,Pixmap,pixmap,id) VAL(5,Atom,atom,id) VAL(6,Cursor,cursor,id) VAL(7,Font,font,id) NOVAL(8,Match,match) VAL(9,Drawable,drawable,id) NOVAL(10,Access,access) NOVAL(11,Alloc,alloc) VAL(12,Colormap,colormap,id) VAL(13,GContext,gcontext,id) VAL(14,IDChoice,idchoice,id) NOVAL(15,Name,name) NOVAL(16,Length,length) NOVAL(17,Implementation,implementation) default: e.type = LX_XE_Other; e.u.other.type = xc->rb[1]; e.u.other.seq = seq; e.u.other.opc_major = major; e.u.other.opc_minor = minor; break; } #undef NOVAL #undef VAL if ( xc->pendf && (xc->last_iserial == xc->pendf->serial) && ( (*xc->pendf->err)(xc,&e,xc->pendf->reqarg) || ( xc->pendf->uerr && (*xc->pendf->uerr)(xc,xc->pendf,&e,xc->pendf->uerrarg) ) ) ) { lx__op_finished(xc->pendf); return; } switch ((*xc->x_err)(xc,&e)) { case LX_X_ERR_IGNORE: return; break; default: fprintf(stderr,"X library: invalid application X error handler return code\n"); // fall through case LX_X_ERR_CRASH: lx_X_err_print(stderr,xc,&e); exit(1); break; } } static void process_reply(LX_CONN *xc) { LX_OP *op; LX_U16 seq; if (xc->rbl < 32) lx_abort(); seq = r_card16(&xc->rb[2]); advance_iseq(xc,seq); if (xc->flags & XCF_FAIL) return; op = xc->pendf; if (! op) { lx__protoerr(xc,"response when nothing expected"); return; } if (op->serial != xc->last_iserial) { lx__protoerr(xc,"response to %04lx -> %08lx when expecting (%04lx)%04lx\n", (unsigned long int)seq, (unsigned long int)xc->last_iserial, (unsigned long int)(op->serial>>16), (unsigned long int)(op->serial&0xffff) ); return; } (*op->done)(xc,xc->rb,op->reqarg,(op->flags&LXOF_NOWRITE)?REPLY_NOWRITE:0); if (op->flags & LXOF_MORE) { op->flags &= ~LXOF_MORE; return; } lx__op_finished(op); } void lx__more_replies(LX_CONN *xc) { xc->pendf->flags |= LXOF_MORE; } int lx__proto_pkt(LX_CONN *xc, const void *data, int len) { const unsigned char *dp; int n; int o; LX_U32 l; if (xc->flags & XCF_FAIL) return(len); dp = data; o = 0; while (o < len) { n = xc->rbw - xc->rbl; if (n < 1) lx_abort(); if (n > len-o) { bcopy(dp+o,xc->rb+xc->rbl,len-o); xc->rbl += len - o; break; } bcopy(dp+o,xc->rb+xc->rbl,n); xc->rbl += n; o += n; switch (xc->rb[0]) { case 0: // Error if (xc->rbl != 32) lx_abort(); process_error(xc); break; case 1: // Reply l = r_card32(xc->rb+4); if (l > 0x3fffffff-8) lx__protoerr(xc,"insanely long reply (length field %lu)\n",(unsigned long int)l); xc->rbw = (l * 4) + 32; if (rb_room(xc,xc->rbw)) { lx__nomem_fail(xc); return(len); } if (xc->rbl < xc->rbw) continue; process_reply(xc); break; default: // Event if (xc->rbl != 32) lx_abort(); lx__process_event(xc); break; } lx__proto_newpkt(xc); } return(len); } void lx__nomem_fail(LX_CONN *xc) { (*xc->lib_err)(xc,&lx__nomem_err); xc->flags |= XCF_FAIL; } void lx__bad_call(LX_CONN *xc, const char *call) { LX_LIB_ERR e; e.type = LX_LE_BAD_CALL; e.u.bad_call.fn = call; (*xc->lib_err)(xc,&e); } void lx__grab_charinfo(LX_CHARINFO *ci, const unsigned char *data) { ci->lbearing = r_int16(data); ci->rbearing = r_int16(data+2); ci->width = r_int16(data+4); ci->ascent = r_int16(data+6); ci->descent = r_int16(data+8); ci->attr = r_card16(data+10); } LX_OP *lx_op_noreply(LX_CONN *xc) { LX_OP *op; if (xc->flags & XCF_FAIL) return(0); op = new_lx_op(xc,xc->last_oserial); if (op == 0) { lx__nomem_fail(xc); return(0); } op->flags |= LXOF_NOREPLY; op_link(op); return(op); }