#include #include #include #include #include #include "lx.h" #include "internal.h" // This file knows we always use big-endian CARD16 r_card16(const unsigned char *pp) { return((pp[0]*256U)+pp[1]); } int r_int16(const unsigned char *pp) { CARD16 v; v = r_card16(pp); if (v & 0x8000) return((int)((int)(v&0x7fff)-0x8000)); else return(v); } void w_card16(unsigned char *pp, CARD16 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, INT16 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] = (CARD16)val >> 8; pp[1] = (CARD16)val & 0xff; } CARD32 r_card32(const unsigned char *pp) { return((pp[0]*16777216U)+(pp[1]*65536U)+(pp[2]*256U)+pp[3]); } void w_card32(unsigned char *pp, CARD32 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] = (CARD32)val >> 24; pp[1] = ((CARD32)val >> 16) & 0xff; pp[2] = ((CARD32)val >> 8) & 0xff; pp[3] = (CARD32)val & 0xff; } int r_int32(const unsigned char *pp) { CARD32 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, CARD32 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 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; } xc->next_serial ++; } 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; } xc->next_serial ++; } 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) { if (op->flags & LXOF_LINK) lx_abort(); op->flink = 0; op->blink = op->conn->pendingb; if (op->blink) op->blink->flink = op; else op->conn->pendingf = op; op->conn->pendingb = op; op->flags |= LXOF_LINK; } static void op_unlink(LX_OP *op) { if (! (op->flags & LXOF_LINK)) lx_abort(); if (op->flink) op->flink->blink = op->blink; else op->conn->pendingb = op->blink; if (op->blink) op->blink->flink = op->flink; else op->conn->pendingf = 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(); if (op->flags & LXOF_DONE) { op_unlink(op); op_free(op); } op->flags = (op->flags & ~LXOF_KEEP) | LXOF_DROP; } 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); } void lx__protoerr(LX_CONN *xc, const char *fmt, ...) { va_list ap; char *s; int l; LX_LIB_ERR e; va_start(ap,fmt); l = vasprintf(&s,fmt,ap); va_end(ap); if (xc->flags & XCF_OPENING) { if (l < 0) { (*xc->os->err)(&lx__nomem_err,xc->os->cbarg); } else { e.type = LXLE_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 = LXLE_PROTO_ERR; e.u.proto_err.msg = s; (*xc->lib_err)(xc,&e); } } xc->flags |= XCF_FAIL; } LX_OP *lx__expect_reply( LX_CONN *xc, const unsigned char *req, int reqlen, void (*done)(LX_OP *, const unsigned char *), void *priv ) { LX_OP *op; CARD32 ser; ser = xc->next_serial; lx__send_req(xc,req,reqlen); if (xc->flags & XCF_FAIL) return(0); op = malloc(sizeof(LX_OP)); if (op == 0) { lx__nomem_fail(xc); return(0); } op->conn = xc; op->serial = ser; op->usercb = 0; op->usercbarg = 0; op->done = done; op->reqpriv = priv; op->flags = LXOF_KEEP; op->udata = 0; 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;iu.field.seq, \ (unsigned int)e->u.field.opc_major, \ (unsigned int)e->u.field.opc_minor); \ break; #define VAL(name,field,valfield,label) \ case LXXE_##name: \ fprintf(stderr,"X error: Bad%s, sequence %04x opcode %u.%u: bad %s %08lx\n", \ #name, \ (unsigned int)e->u.field.seq, \ (unsigned int)e->u.field.opc_major, \ (unsigned int)e->u.field.opc_minor, \ label, (unsigned long int)e->u.field.valfield); \ break; switch (e->type) { NOVAL(Request,request) VAL(Value,value,value,"value") VAL(Window,window,id,"window id") VAL(Pixmap,pixmap,id,"pixmap id") VAL(Atom,atom,id,"atom id") VAL(Cursor,cursor,id,"cursor id") VAL(Font,font,id,"font id") NOVAL(Match,match) VAL(Drawable,drawable,id,"drawable id") NOVAL(Access,access) NOVAL(Alloc,alloc) VAL(Colormap,colormap,id,"colormap id") VAL(GContext,gcontext,id,"GC id") VAL(IDChoice,idchoice,id,"id") NOVAL(Name,name) NOVAL(Length,length) NOVAL(Implementation,implementation) case LXXE_Other: fprintf(stderr,"X error: type %u, sequence %04x, opcode %u.%u\n", e->u.other.type,e->u.other.seq,e->u.other.opc_major,e->u.other.opc_minor); break; } #undef NOVAL #undef VAL exit(1); } // XXX Figure out how to cancel LX_OP for an erroring request! static void process_error(LX_CONN *xc) { LX_X_ERR e; CARD16 seq; CARD32 val; CARD16 minor; CARD8 major; if (xc->rbl != 32) lx_abort(); seq = r_card16(xc->rb+2); 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 = LXXE_##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 = LXXE_##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 = LXXE_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 switch ((*xc->x_err)(xc,&e)) { case LX_X_ERR_IGNORE: return; break; case LX_X_ERR_CRASH: x_err_crash(&e); break; default: fprintf(stderr,"X library: invalid application X error handler return code\n"); exit(1); break; } } static void process_reply(LX_CONN *xc) { LX_OP *op; CARD16 seq; if (xc->rbl < 32) lx_abort(); op = xc->pendingf; if (! op) { lx__protoerr(xc,"response when expecting nothing"); return; } seq = r_card16(&xc->rb[2]); if (seq != (op->serial&0xffff)) lx__protoerr(xc,"response to %04x when expecting (%04x)%04x\n",seq,op->serial>>16,op->serial&0xffff); (*op->done)(op,xc->rb); if (op->flags & LXOF_MORE) { op->flags &= ~LXOF_MORE; } else if (op->flags & LXOF_DROP) { op_unlink(op); op_free(op); } else { if (op->usercb) (*op->usercb)(op->usercbarg); op->flags |= LXOF_DONE; if (! (op->flags & LXOF_KEEP)) { op_unlink(op); op_free(op); } } } int lx__proto_pkt(LX_CONN *xc, const void *data, int len) { const unsigned char *dp; int n; int o; CARD32 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 = LXLE_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); }