chiark / gitweb /
can fit 139 lines on an atp -B page
[trains.git] / hostside / obc.c
1 /*
2  * daemons
3  * output buffer chains
4  */
5
6 #include <assert.h>
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <stdarg.h>
10 #include <string.h>
11
12 #include "daemons.h"
13 #include "../layout/dlist.h"
14
15 struct OutBuffer {
16   OutBuffer *back, *next;
17   char *m;
18   int l;
19 };
20
21 int obc_tryflush(OutBufferChain *ch) {
22   OutBuffer *ob;
23   int r;
24   
25   for (;;) {
26     ob= ch->obs.head;
27     if (!ob)
28       return 0;
29     if (ch->done_of_head == ob->l) {
30       LIST_UNLINK(ch->obs, ob);
31       free(ob->m);
32       free(ob);
33       ch->done_of_head= 0;
34       continue;
35     }
36     r= write(ch->fd, ob->m + ch->done_of_head, ob->l - ch->done_of_head);
37     if (r==-1) {
38       if (errno==EINTR) continue;
39       if (errno==EWOULDBLOCK) return errno;
40       ch->error(ch,"write",strerror(errno));
41       return errno;
42     }
43     assert(r>=0);
44     ch->done_of_head += r;
45     ch->total -= r;
46     assert(ch->done_of_head <= ob->l);
47   }
48 }
49
50 static void *writeable(oop_source *evts, int fd,
51                        oop_event evt, void *ch_v) {
52   OutBufferChain *ch= ch_v;
53   assert(fd == ch->fd);
54   assert(evt == OOP_WRITE);
55   obc_tryflush(ch);
56   if (evts && !ch->obs.head) {
57     events->cancel_fd(events, ch->fd, OOP_WRITE);
58     ch->done_of_head= -1;
59     if (ch->empty) ch->empty(ch);
60   }
61   return OOP_CONTINUE;
62 }
63
64 static void addlink(OutBufferChain *ch, OutBuffer *ob,
65                     CopyCallBack *ccb, void *ccbu) {
66   if (ccb) ccb(ob->m,ob->l,ccbu);
67
68   if (ch->done_of_head < 0) {
69     assert(!ch->obs.head);
70     if (events) /* in simulation, events==0 */
71       events->on_fd(events, ch->fd, OOP_WRITE, writeable, ch);
72     ch->done_of_head= 0;
73   }
74     
75   LIST_LINK_TAIL(ch->obs, ob);
76   ch->total += ob->l;
77   if (ob->l>0 && ob->m[ob->l-1]=='\n')
78     obc_tryflush(ch);
79   if (ch->total > ch->limit) {
80     char what[128];
81     snprintf(what,sizeof(what)-1,"`%.*s...'", ob->l,ob->m);
82     what[sizeof(what)-1]= 0;
83     obc_tryflush(ch);
84     ch->error(ch,"buffer limit exceeded",what);
85   }
86 }
87
88 void obc_init_core(OutBufferChain *ch) {
89   ch->done_of_head= -1;
90   ch->total= 0;
91   if (!ch->limit) ch->limit= 128*1024;
92   LIST_INIT(ch->obs);
93 }
94
95 void obc_init(OutBufferChain *ch) {
96   int r;
97   obc_init_core(ch);
98   r= oop_fd_nonblock(ch->fd, 1);
99   if (r) diee("nonblock(OutBufferChain->fd,1)");
100 }
101
102 void ovprintf_ccb(OutBufferChain *ch, CopyCallBack *ccb, void *ccbu,
103                    const char *fmt, va_list al) {
104   OutBuffer *ob;
105
106   ob= mmalloc(sizeof(*ob));
107   ob->l= vasprintf(&ob->m, fmt, al);
108   if (ob->l < 0) diem();
109   if (!ob->l) { free(ob->m); free(ob); return; }
110   addlink(ch,ob,ccb,ccbu);
111 }
112
113 void ovprintf(OutBufferChain *ch, const char *fmt, va_list al) {
114   ovprintf_ccb(ch,0,0,fmt,al);
115 }
116
117 void oprintf(OutBufferChain *ch, const char *msg, ...) {
118   va_list al;
119   va_start(al,msg);
120   ovprintf(ch,msg,al);
121   va_end(al);
122 }
123
124 void voerror(OutBufferChain *ch, const char *fmt, va_list al) {
125   oprintf(ch,"error ");
126   ovprintf(ch,fmt,al);
127   owrite(ch,"\n",1);
128 }
129
130 void owrite(OutBufferChain *ch, const char *data, int l) {
131   OutBuffer *ob;
132   ob= mmalloc(sizeof(*ob));
133   ob->l= l;
134   ob->m= mmalloc(l);
135   memcpy(ob->m, data, l);
136   addlink(ch,ob,0,0);
137 }