Added very primitive documentation files.
[vblib.git] / vblib.c
1 #include <stdlib.h>
2 #include <syslog.h>
3 #include <signal.h>
4 #include <assert.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <stdio.h>
8 #include <stdarg.h>
9 #include <netinet/in.h>
10 #include <linux/un.h>
11 #include <sys/types.h>
12 #include <sys/wait.h>
13 #include <fcntl.h>
14 #include <sys/poll.h>
15 #include <slang.h>
16 #include <time.h>
17 #include <sys/time.h>
18 #include <limits.h>
19 #include <sys/file.h>
20 #include <sys/resource.h>
21 #include <fnmatch.h>
22 #include <setjmp.h>
23 #include <sys/socket.h>
24 #include <stddef.h>
25 #ifdef HAVE_PTHREAD
26 #include <pthread.h>
27 #endif
28
29 #include "vblib.h"
30
31 const int safesigs[]={SIGQUIT,SIGINT,SIGTERM,SIGIOT,SIGALRM,-1}; //SIGCHLD->childsignal anyway
32 char vbnexact;
33
34 void chk(const void *p)
35 {
36         vbsanity();
37         if (p) return;
38         VB_FATAL(CRIT,"NULL-check failure, memory exhausted? FATAL: %m");
39 }
40
41 #ifndef NDEBUG
42 static void *vbchecknull=NULL;
43 static struct varbuf *vbcheckhead=(struct varbuf *)&vbchecknull;
44 static unsigned vbchecknum;
45 #ifdef FATALSIG_INSANE
46 static char vbcheckno;
47 #else
48 #define vbcheckno (0)
49 #endif
50
51 #ifdef HAVE_PTHREAD
52 static pthread_mutex_t vbcheckmx=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
53 static void vbchecklock  (void)
54 { if (pthread_mutex_lock  (&vbcheckmx)) VB_FATAL(CRIT,"Mutex ""lock on vbcheck mx"); }
55 static void vbcheckunlock(void)
56 { if (pthread_mutex_unlock(&vbcheckmx)) VB_FATAL(CRIT,"Mutex unlock on vbcheck mx"); }
57 #else
58 #define vbchecklock()
59 #define vbcheckunlock()
60 #endif
61
62 static char vbchecklist(struct varbuf *vb)
63 {
64 struct varbuf *vbs;
65 unsigned tot=0;
66
67         vbchecklock();
68         assert(!vbchecknull);
69         assert(!!vb);
70         for (vbs=vbcheckhead;vbs;vbs=vbs->checknext,tot++)
71                 if (vb==vbs) break;
72         if (!vbs) {
73                 vbcheckunlock();
74                 return(0);
75                 }
76         assert(vb->checknext);
77         while (tot++,(vbs=vbs->checknext)!=vbchecknull) assert(vb!=vbs);
78         assert(tot==vbchecknum+1);
79         vbcheckunlock();
80         return(1);
81 }
82
83 void vbcheck(struct varbuf *vb)
84 {
85 struct varbufnode *vbn;
86 char *s;
87 volatile char touch;
88
89         if (vbcheckno) return; vbchecklock();
90         assert(vbchecklist(vb));
91         if (vb->done) assert(!!vb->f);
92         if (vb->free) assert(!!vb->l);
93         assert((!vb->f)==(!vb->f));
94         for (vbn=vb->f;vbn;vbn=vbn->next) {
95                 if (vbn==vb->f) assert(vb->done<=vbn->size);
96                 if (vbn==vb->l) assert(vb->free<=vbn->size);
97                 for (s=vbn->buf;s<vbn->buf+vbn->size;s++) touch=*s;
98                 }
99         vbcheckunlock();
100 }
101
102 void vbsanity(void)
103 {
104 struct varbuf *vb;
105         dbg("vbsanity()\n");
106         if (vbcheckno) return; vbchecklock();
107         assert(!vbchecknull);
108         for (vb=vbcheckhead;vb!=(struct varbuf *)&vbchecknull;vb=vb->checknext) vbcheck(vb);
109         vbcheckunlock();
110 }
111 #endif
112
113 #ifdef VBDEBUG
114 void vbdebug(struct varbuf *vb,const char *msg)
115 {
116 struct varbufnode *vbn;
117         fprintf(stderr,"vbdebug(%s): vb=%p",msg,vb);
118         vbcheck(vb);
119         if (!vb) {
120                 fprintf(stderr,"\n");
121                 return;
122                 }
123         fprintf(stderr,", first=%p, last=%p, done=%u, free=%u\n",vb->f,vb->l,vb->done,vb->free);
124         for (vbn=vb->f;vbn;vbn=vbn->next) {
125 size_t i;
126                 fprintf(stderr," *VBN: %p: size=%u, buf:",vbn,vbn->size);
127                 for (i=0;i<vbn->size;i++) {
128 char c=vbn->buf[i];
129                         if (isprint(c)) fprintf(stderr," '%c'",c);
130                         else fprintf(stderr," %02x",(unsigned char)c);
131                         }
132                 fprintf(stderr,"\n");
133                 }
134         fprintf(stderr,"vbdebug(%s): finishing...\n",msg);
135 }
136 #endif
137
138 struct varbufnode *vbnnew(size_t sugsize)
139 {
140 struct varbufnode *r;
141         if (!vbnexact && sugsize<VB_DEFSIZE) sugsize=VB_DEFSIZE;
142         chk(r=malloc(sizeof(*r)+sugsize));
143         r->next=NULL;
144         r->size=sugsize;
145         return(r);
146 }
147
148 void vbgrow(struct varbuf *vb,size_t sugsize)
149 {
150 struct varbufnode *vbn;
151         vbdebug(vb,"vbgrow");
152         assert(!vb->free);
153         if (!vbnexact) {
154                 if (vb->l && vb->l->size>sugsize) sugsize=vb->l->size;
155                 if (sugsize*2<=VB_MAXSIZE) sugsize*=2;
156                 else if (sugsize<VB_MAXSIZE) sugsize=VB_MAXSIZE;
157                 }
158         vbn=vbnnew(sugsize);
159         if (vb->l) vb->l->next=vbn;
160         else vb->f=vbn;
161         vb->l=vbn;
162         vb->free=vbn->size;
163 }
164
165 void vbinit(struct varbuf *vb)
166 {
167 #ifndef NDEBUG
168         if (!vbcheckno) assert(!vbchecklist(vb));
169 #endif
170         bzero(vb,sizeof(*vb));
171 #ifndef NDEBUG
172         vbchecklock();
173         vb->checknext=vbcheckhead;
174         vbcheckhead=vb;
175         vbchecknum++;
176         vbcheckunlock();
177 #endif
178         vbcheck(vb);
179 }
180
181 struct varbufnode *vbremone(struct varbuf *vb)
182 {
183 struct varbufnode *r;
184         vbdebug(vb,"vbremone");
185         assert(vb->f);
186         r=vb->f->next;
187         free(vb->f);
188         vb->done=0;
189         if (!(vb->f=r)) {
190                 vb->l=NULL;
191                 vb->free=0;
192                 }
193         return(r);
194 }
195
196 void vbclear(struct varbuf *vb)
197 {
198         vbdebug(vb,"vbclear");
199         while (vb->f) vbremone(vb);
200 }
201
202 void vbrem(struct varbuf *vb)
203 {
204 #ifndef NDEBUG
205 struct varbuf **vbp;
206 #endif
207
208         vbdebug(vb,"vbrem");
209         vbclear(vb);
210 #ifndef NDEBUG
211         vbchecklock();
212         assert(vbchecknum>0);
213         for (vbp=&vbcheckhead;*vbp;vbp=&(*vbp)->checknext)
214                 if (*vbp==vb) break;
215         *vbp=vb->checknext;
216         vbchecknum--;
217         bzero(vb,sizeof(*vb));
218         vbcheckunlock();
219 #endif
220 }
221
222 size_t vbsize(struct varbuf *vb,size_t maxsize)
223 {
224 size_t size=0;
225 struct varbufnode *vbn=vb->f;
226
227         vbdebug(vb,"vbsize");
228         while (vbn && (!maxsize || size<maxsize)) {
229                 size+=VBNSIZE(vb,vbn);
230                 vbn=vbn->next;
231                 }
232         return(size);
233 }
234
235 #define GENVARBUFC(argmid) \
236         void vbput##argmid(struct varbuf *vb,const argmid arg) \
237         { vbwrite(vb,&arg,sizeof(arg)); } \
238         char vbget##argmid(struct varbuf *vb,argmid *argp) \
239         { if (sizeof(*argp)>1 && !VBCHKSIZE(vb,sizeof(*argp))) return(0); \
240         return(vbread(vb,argp,sizeof(*argp))); }
241
242 #ifndef INLINE_PUTGETCHAR
243 GENVARBUFC(char)
244 #endif
245 GENVARBUFC(short)
246 GENVARBUFC(int)
247
248 #undef GENVARBUFC
249
250 void vbwrite(struct varbuf *vb,const void *buf,size_t count)
251 {
252 size_t now;
253
254         vbdebug(vb,"vbwrite");
255         if (!count) return;
256         if (vb->free) {
257                 assert(vb->l);
258                 memcpy(vb->l->buf+VBNSIZEL(vb,vb->l),buf,(now=min(vb->free,count)));
259                 count-=now;
260                 vb->free-=now;
261                 if (!count) return;
262                 buf+=now;
263                 }
264         vbgrow(vb,count);
265         assert(vb->free>=count);
266         assert(vb->l&&vb->l->size==vb->free);
267         memcpy(vb->l->buf,buf,count);
268         vb->free-=count;
269         assert(vb->free>=0);
270 }
271
272 extern size_t vbputstring(struct varbuf *vb,const char *s)
273 {
274 size_t l=strlen(s);
275         vbwrite(vb,s,l);
276         return(l);
277 }
278
279 ssize_t vbwritefd(struct varbuf *vb,int fd)
280 {
281 ssize_t now,got=0;
282
283         vbdebug(vb,"vbwritefd");
284         for (;;) {
285                 if (!vb->free) vbgrow(vb,0);
286                 now=read(fd,vb->l->buf+vb->l->size-vb->free,vb->free);
287                 dbg("vbwritefd: now=%d\n",now);
288                 if (now<0) {
289                         if (errno==EAGAIN) break;
290                         return(now);
291                         }
292                 if (!now) {
293                         errno=ENODATA;
294                         return(-1);
295                         }
296                 vb->free-=now;
297                 assert(vb->free>=0);
298         }
299         return(got);
300 }
301
302 size_t vbpeek(struct varbuf *vb,ssize_t offs,void *buf,size_t count)
303 {
304 size_t got=0,now,size;
305 char doread;
306 struct varbufnode *vbn=vb->f;
307
308         vbdebug(vb,"vbpeek");
309         if ((doread=(offs==-1))) offs=0;
310         offs+=vb->done;
311         while (vbn && count) {
312                 size=VBNSIZEL(vb,vbn);
313                 if (offs<size) {
314                         memcpy(buf,vbn->buf+offs,(now=min(count,size-offs)));
315                         buf+=now;
316                         count-=now;
317                         got+=now;
318                         offs+=now;
319                         if (doread) {
320                                 assert(vbn==vb->f);
321                                 vb->done+=now;
322                                 }
323                         }
324                 offs-=size;
325                 if (!VBNSIZE(vb,vb->f)) {
326                         assert(doread);
327                         vbremone(vb);
328                         vbn=vb->f;
329                         }
330                 else vbn=vbn->next;
331                 }
332         return(got);
333 }
334
335 ssize_t vbchrn(struct varbuf *vb,ssize_t offs,char c,char dir)
336 {
337 struct vbl {
338         struct vbl *next;
339         struct varbufnode *vbn;
340         } *vbl=NULL,*vbl2;
341 struct varbufnode *vbn;
342 ssize_t distoffs=0,size=vbsize(vb,0);
343
344         assert(!!dir);
345         if (offs==-1 || offs>=size) {
346                 if (dir>0) return(-1);
347                 else offs=size-1;
348                 }
349         if (offs<0) return(-1);
350         vbn=vb->f;
351         offs+=vb->done;
352         distoffs=-vb->done;
353         while (vbn && offs>=vbn->size) {
354                 if (dir<0) {
355                         chk(vbl2=alloca(sizeof(*vbl2)));
356                         vbl2->next=vbl;
357                         vbl2->vbn=vbn;
358                         vbl=vbl2;
359                         }
360                 offs-=vbn->size;
361                 assert(offs>=0);
362                 distoffs+=vbn->size;
363                 vbn=vbn->next;
364                 }
365         if (dir<0) for (;;) {
366                 if (vbn && offs<VBNSIZEL(vb,vbn)) {
367 ssize_t start=VBNSTART(vb,vbn); // FIXME: not really - GCC bug! (size_t crashes)
368                         dbg("seeking back... start=%u,offs=%d,vbn->size=%u\n",start,offs,vbn->size);
369                         while (offs>=start&&vbn->buf[offs]!=c) offs--;
370                         if (offs>=start) return(distoffs+offs);
371                         }
372                 if (!vbl) break;
373                 vbn=vbl->vbn;
374                 vbl=vbl->next;
375                 distoffs-=vbn->size;
376                 offs=vbn->size-1;
377                 assert(offs>=0);
378                 }
379         if (dir>0) while (vbn) {
380 size_t size=VBNSIZEL(vb,vbn);
381 char *s;
382                 dbg("vbn=%p,offs=%d,size=%d\n",vbn,offs,size);
383                 assert(offs<=size);
384                 if ((s=memchr(vbn->buf+offs,c,size-offs)))
385                         return(distoffs+(s-vbn->buf));
386                 distoffs+=vbn->size;
387                 vbn=vbn->next;
388                 offs=0;
389                 }
390         return(-1);
391 }
392
393 ssize_t vbreadfd(struct varbuf *vb,int fd)
394 {
395 ssize_t now,got=0;
396
397         vbdebug(vb,"vbreadfd");
398         dbg("entering vbreadfd(fd=%d)\n",fd);
399         while (vb->f) {
400 size_t size=VBNSIZE(vb,vb->f);
401                 now=write(fd,vb->f->buf+vb->done,size);
402                 dbg("vbreadfd: now=%d\n",now);
403                 if (now<0 && errno!=EAGAIN) return(now);
404                 if (now<=0) return(got);
405                 vb->done+=now;
406                 assert(vb->done<=size);
407                 got+=now;
408                 if (!VBNSIZE(vb,vb->f))
409                         vbremone(vb);
410                 }
411         return(got);
412 }
413
414 char quiet_null;
415
416 static void fixupnulls(char *s,size_t len,const char *fname)
417 {
418 unsigned tot=0;
419 char *d;
420
421         for (d=s;(d=memchr(d,'\0',s+len-d));) { *d++=' '; tot++; }
422         if (!tot) return;
423 #define ARG "%s() replaced %u '\\0's by ' ' in: %.*s",fname,tot,(int)len,s
424 #define VB_LOG1(a,b) VB_LOG(a,b)
425         if (quiet_null)
426                 VB_LOG1(DEBUG,ARG);
427         else
428                 VB_LOG1(ERR,ARG);
429 #undef VB_LOG1
430 #undef ARG
431 }
432
433 char *vbgetline(struct varbuf *vb,char term)
434 {
435 size_t len,r;
436 char *s;
437
438         if (!(len=vbchrn(vb,0,term,1)+1)) return(NULL);
439         assert(len>0);
440         chk(s=malloc(len));
441         r=vbread(vb,s,len); assert(r==len);
442         assert(s[len-1]==term);
443         s[len-1]='\0';
444         fixupnulls(s,len-1,"vbgetline");
445         return(s);
446 }
447
448 size_t chkvsprintf(char **sp,const char *fmt,va_list ap)
449 {
450 size_t bufsize=VB_DEFSIZE;
451 int i;
452
453         for (*sp=NULL;;) {
454                 chk(*sp=realloc(*sp,bufsize));
455                 i=vsnprintf(*sp,bufsize,fmt,ap);
456                 dbg("chkvsprintf: bufsize=%u,i=%d,fmt:%s\n",bufsize,i,fmt);
457                 if (i>=0&&i<bufsize) break;
458                 bufsize*=2;
459                 }
460         assert(i>=0);
461         return(i);
462 }
463
464 size_t vbvprintf(struct varbuf *vb,const char *fmt,va_list ap,char remlf)
465 {
466 char *buf=NULL;
467 size_t len;
468
469         len=chkvsprintf(&buf,fmt,ap);
470         if (remlf) {
471 char *s,c;
472                 for (s=buf;(c=*s);s++) {
473                         if (c<0 || (c>=' '&&c!='\\')) {
474                                 vbputchar(vb,c);
475                                 continue;
476                                 }
477                         vbputchar(vb,'\\');
478                         switch (c) {
479                                 case '\n'     : vbputchar(vb,'n');      break;
480                                 case '\r'     : vbputchar(vb,'r');      break;
481                                 case  1 ...  9: //FALLTHRU
482                                 case 11 ... 12: //FALLTHRU
483                                 case 14 ... 31: vbprintf(vb,"x%02X",c); break;
484                                 case '\\'    :  vbputchar(vb,'\\');     break;
485                                 default: assert(0);
486                                 }
487                         }
488                 }
489         else vbwrite(vb,buf,len);
490         free(buf);
491         return(len);
492 }
493
494 size_t vbprintf(struct varbuf *vb,const char *fmt,...) //stub
495 {
496 va_list ap;
497 size_t r;
498
499         va_start(ap,fmt);
500         r=vbvprintf(vb,fmt,ap,0);
501         va_end(ap);
502         return(r);
503 }
504
505 #ifdef PSTR_16BIT
506 #define PSTR_MAX 65535
507 #define PSTR_TYPE short
508 #else
509 #define PSTR_MAX 255
510 #define PSTR_TYPE char
511 #endif
512
513 #define PSTR_PUTLEN2(vb,len,type) vbput##type((vb),(type)(len))
514 #define PSTR_PUTLEN1(vb,len,type) PSTR_PUTLEN2(vb,len,type)
515 #define PSTR_PUTLEN(vb,len) PSTR_PUTLEN1((vb),(len),PSTR_TYPE)
516
517 void vbpstrputstring(struct varbuf *vb,const char *s)
518 {
519 size_t len=strlen(s);
520         if (len>PSTR_MAX) {
521                 VB_LOG(CRIT,"String longer (%u) than PSTRing max (%u)",len,PSTR_MAX);
522                 len=PSTR_MAX;
523                 dbg("vbpstrputstring-longer than MAX: \"%s\"\n",s);
524                 }
525         PSTR_PUTLEN(vb,len);
526         vbwrite(vb,s,len);
527 }
528
529 size_t vbpstrcopy(struct varbuf *vbd,struct varbuf *vbs,ssize_t len)
530 {
531 size_t size,was;
532         if (len==-1) len=vbsize(vbs,0);
533         else if ((size=vbsize(vbs,len))<len) len=size;
534         was=len;
535         if (len>PSTR_MAX) {
536                 VB_LOG(CRIT,"String longer (%u) than PSTRing max (%u)",len,PSTR_MAX);
537                 len=PSTR_MAX;
538                 vbdebug(vbs,"vbpstrputstring-longer than MAX");
539                 }
540         PSTR_PUTLEN(vbd,len);
541         vbcopy(vbd,vbs,len);
542         if (was!=len) vbdrop(vbs,was-len);
543         return(was);
544 }
545
546 void vbpstrprintf(struct varbuf *vb,const char *fmt,...)
547 {
548 va_list ap;
549 char buf[PSTR_MAX+1];
550 int i;
551
552         va_start(ap,fmt);
553         i=vsnprintf(buf,sizeof(buf),fmt,ap);
554         va_end(ap);
555         if (i==-1) {
556                 VB_LOG(CRIT,"Buffer overflow (max=%u) during PSTR printf",PSTR_MAX);
557                 i=PSTR_MAX;
558                 }
559         assert(i>=0 && i<=PSTR_MAX && !buf[i]);
560         vbpstrputstring(vb,buf);
561 }
562
563 char *vbpstrread(struct varbuf *vb)
564 {
565 unsigned PSTR_TYPE len,scan,chgd=0;
566 char *buf;
567 size_t r;
568
569         if (!vbpeek(vb,0,&len,sizeof(len))) return(NULL);
570         if (!VBCHKSIZE(vb,1+len)) return(NULL);
571         chk(buf=malloc(len+1));
572         vbdrop(vb,sizeof(len));
573         r=vbread(vb,buf,len); assert(r==len);
574         buf[len]='\0';
575         for (scan=0;scan<len;scan++)
576                 if (!buf[scan]) {
577                         buf[scan]='?';
578                         chgd++;
579                         }
580         if (chgd)
581                 VB_LOG(CRIT,"'\\0's found (%u total) in read PSTR \"%s\", changed to '?'",chgd,buf);
582         return(buf);
583 }
584
585 size_t vbcopy(struct varbuf *vbd,struct varbuf *vbs,ssize_t count)
586 {
587 size_t now,got,r;
588
589         vbdebug(vbd,"vbcopy-dest");
590         vbdebug(vbs,"vbcopy-src");
591         dbg("vbcopy-count=%d\n",count);
592         got=vbsize(vbs,(count==-1?0:count));
593         if (count==-1 || count>got) count=got;
594         else got=count;
595         dbg("got=%u, vbd->free=%u, count=%d\n",got,vbd->free,count);
596         if (!count) return(got);
597         if (vbd->free) {
598                 dbg("vbd->free=%d path\n",vbd->free);
599                 vbdebug(vbd,"vbd->free-s:vbd");
600                 assert(vbd->f && vbd->l);
601                 r=vbread(vbs,vbd->l->buf+VBNSIZEL(vbd,vbd->l),(now=min(count,vbd->free))); assert(r==now);
602                 dbg("now=%u,vbd->free=%u,count=%u\n",now,vbd->free,count);
603                 vbd->free-=now;
604                 count-=now;
605                 vbdebug(vbd,"vbd->free-e:vbd");
606                 if (!count) return(got);
607                 assert(!vbd->free);
608                 }
609         dbg("vbs->done=%u\n",vbs->done);
610         assert(!vbd->free);
611         assert(!VBEMPTY(vbs));
612         if (vbs->done) {
613                 dbg("vbs->done=%d path\n",vbs->done);
614                 vbdebug(vbs,"vbs->done-s:vbs");
615                 vbdebug(vbd,"vbs->done-s:vbd");
616                 vbnexact=1;
617                 vbgrow(vbd,(now=min(count,VBNSIZE(vbs,vbs->f))));
618                 vbnexact=0;
619                 assert(vbd->l && vbd->l->size==now && vbd->free==now);
620                 r=vbread(vbs,vbd->l->buf,now); assert(r==now);
621                 vbd->free=0;
622                 count-=now;
623                 assert(count>=0);
624                 dbg("in vbs->done: count=%d, now=%u\n",count,now);
625                 vbdebug(vbs,"vbs->done-e:vbs");
626                 vbdebug(vbd,"vbs->done-e:vbd");
627                 if (!count) return(got);
628                 }
629         assert(!vbs->done);
630         vbdebug(vbs,"relink:vbs");
631         vbdebug(vbd,"relink:vbd");
632         while (count && VBNSIZEL(vbs,vbs->f)<=count) {
633 struct varbufnode *vbn=vbs->f;
634                 assert(!vbd->free);
635                 if (!(vbs->f=vbn->next)) {
636                         vbs->l=NULL;
637                         count+=(vbd->free=vbs->free);
638                         vbs->free=0;
639                         }
640                 count-=vbn->size;
641                 if (vbd->l) {
642                         vbd->l->next=vbn;
643                         vbd->l=vbn;
644                         }
645                 else {
646                         assert(!vbd->f);
647                         vbd->f=vbd->l=vbn;
648                         }
649                 vbn->next=NULL;
650                 }
651         if (!count) return(got);
652         assert(!vbd->free);
653         vbdebug(vbs,"final:vbs");
654         vbdebug(vbd,"final:vbd");
655         vbgrow(vbd,count);
656         assert(vbd->l && vbd->l->size>=count && vbd->free==vbd->l->size);
657         vbread(vbs,vbd->l->buf,count);
658         vbd->free-=count;
659         assert(vbd->free>=0);
660         return(got);
661 }
662
663 unsigned short vbxsum(struct varbuf *vb)
664 {
665 unsigned short xsum=0;
666 size_t offs;
667 struct varbufnode *vbn;
668
669         vbdebug(vb,"vbxsum");
670         vbn=vb->f;
671         offs=vb->done;
672         while (vbn) {
673 size_t size=VBNSIZEL(vb,vbn);
674                 assert(offs<size);
675                 xsum+=(unsigned char)vbn->buf[offs];
676                 if (++offs>=size) {
677                         vbn=vbn->next;
678                         offs=0;
679                         }
680                 }
681         dbg("vbxsum=%u\n",(unsigned)xsum);
682         return(xsum);
683 }
684
685 char vbremlast(struct varbuf *vb)
686 {
687 struct varbufnode *vbn;
688
689         vbdebug(vb,"vbremlast");
690         if (VBEMPTY(vb)) return(0);
691         assert(vb->l&&vb->free<=vb->l->size);
692         if (vb->free<vb->l->size) {
693                 vb->free++;
694                 vbdebug(vb,"vbremlast-stdend");
695                 return(1);
696                 }
697         assert(vb->f!=vb->l);
698         for (vbn=vb->f;vbn->next!=vb->l;vbn=vbn->next)
699                 assert(vbn->next);
700         vbn->next=NULL;
701         free(vb->l);
702         vb->l=vbn;
703         assert(vb->l->size);
704         vb->free=1;
705         vbdebug(vb,"vbremlast-blasting-end");
706         return(1);
707 }
708
709 void vbdrop(struct varbuf *vb,size_t len)
710 {
711 struct varbufnode *vbn;
712
713         assert(VBCHKSIZE(vb,len));
714         vb->done+=len;
715         while (vb->f&&vb->done>=VBNSIZEL(vb,vb->f)) {
716                 vb->done-=VBNSIZEL(vb,vb->f);
717                 vbn=vb->f;
718                 if (!(vb->f=vbn->next)) {
719                         assert(vbn==vb->l);
720                         vb->l=NULL;
721                         vb->free=0;
722                         }
723                 free(vbn);
724                 }
725         if (!vb->f) assert(!vb->done&&!vb->free&&!vb->l);
726 }
727
728 void vbgetlinef(struct varbuf *vb,FILE *f)
729 {
730 char buf[LOGF_FGETS_BUF],*s;
731
732         dbg("vbgetlinef(), tell=0x%lx\n",ftell(f));
733         for (;;) {
734                 if (!fgets(buf,sizeof(buf),f)) {
735                         dbg("vbgetlinef: fgets=NULL\n");
736                         if (feof(f)) return;
737                         VB_FATAL(ERR,"linereading fail: %m");
738                         }
739                 dbg("vbgetlinef: fgets: %s\n",buf);
740                 if ((s=strchr(buf,'\n'))) break;
741                 vbputstring(vb,buf);
742                 }
743         vbwrite(vb,buf,s-buf);
744         dbg("vbgetlinef(f), ftell=0x%lx\n",ftell(f));
745 }
746
747 ssize_t vbcopyline(struct varbuf *vbd,struct varbuf *vbs)
748 {
749 ssize_t len,r;
750         if ((len=vbchrn(vbs,0,'\n',1))==-1) return(-1);
751         r=vbcopy(vbd,vbs,len); assert(r==len);
752         vbdrop(vbs,1);
753         return(len);
754 }
755
756 char vbdropline(struct varbuf *vb)
757 {
758 ssize_t len;
759         if ((len=vbchrn(vb,0,'\n',1))==-1) return(0);
760         vbdrop(vb,len+1);
761         return(1);
762 }
763
764 char *vbmemorize(struct varbuf *vb,unsigned flg,size_t *sizep)
765 {
766 ssize_t len=vbsize(vb,0),r;
767 char *s;
768         if (sizep) *sizep=len;
769         chk(s=malloc(len+!!(flg&VBMEM_ZERO)));
770         r=vbpeek(vb,(flg&VBMEM_REM?-1:0),s,len); assert(r==len);
771         if (flg&VBMEM_ZERO) {
772                         s[len]='\0';
773                 fixupnulls(s,len,"vbmemorize");
774                 }
775         if (flg&VBMEM_REM) assert(VBEMPTY(vb));
776         return(s);
777 }
778
779 char *vbstringify(struct varbuf *vb,char rem)
780 { return vbmemorize(vb,VBMEM_ZERO|(rem?VBMEM_REM:0),NULL); }
781
782 const char *signames[]={
783         "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "IOT", "BUS", "FPE", "KILL",
784         "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT", "CHLD", "CONT",
785         "STOP", "TSTP", "TTIN", "TTOU", "URG", "XCPU", "XFSZ", "VTALRM", "PROF",
786         "WINCH", "IO", "PWR", "UNUSED" };
787
788 void abortsignal(int signo)
789 { //no reinstall! (deadlock!)
790 #ifdef FATALSIG_INSANE
791         vbcheckno=1;
792 #endif
793         signal(signo,SIG_DFL);
794         dbg("Got signal %d\n",signo);
795         exit(EXIT_FAILURE);
796 }
797
798 struct varbuf *chat_vbout;
799 int chat_cmdpar;
800 char *chat_cmdpars[CHAT_CMD_MAXPARS];
801
802 static void chatfn_help(void);
803
804 void chatfn_quit(void) __WEAK;
805 void chatfn_quit(void)
806 {
807         VB_LOG(CRIT,"chatfn_quit() not initialized");
808 }
809
810 static const struct chat_cmdtab  chat_cmdint[]={
811         {"help"    ,0,chatfn_help    ,"This help"},
812         {"quit"    ,0,chatfn_quit    ,"Close connection"},
813         {"exit"    ,0,chatfn_quit    ,"Close connection"},
814 };
815
816 const struct chat_cmdtab chat_cmdtable[] __WEAK =
817         {{NULL,0,NULL,NULL}};
818
819 static void chatfn_help(void)
820 {
821 int i;
822 const struct chat_cmdtab *cmd,*cmdt;
823
824         vbputstring(chat_vbout,"000 Available commands:\n");
825         cmdt=chat_cmdtable;
826         for (i=0;i<NELEM(chat_cmdint);) {
827                 if (cmdt->name) cmd=cmdt++;
828                 else cmd=&chat_cmdint[i++];
829                 vbprintf(chat_vbout,"000 %-10s[%d] - %s\n",cmd->name,(int)cmd->npars,cmd->help);
830                 }
831         vbputstring(chat_vbout,"102 Help end.\n");
832 }
833
834 static inline void chat_parsepars(char *s)
835 {
836 char c,*d,quote;
837
838         chat_cmdpar=0;
839         if (*s) for (*s++='\0';*s&&isspace(*s);s++);
840         for (c=*s;c&&chat_cmdpar<CHAT_CMD_MAXPARS;chat_cmdpar++) {
841                 quote=0;
842                 for (chat_cmdpars[chat_cmdpar]=d=s;*s&&(quote||(!quote&&*s!=','));s++) {
843                         if (*s=='"'||*s=='\'') {
844                                 if (quote==*s) { quote=0; continue; }
845                                 if (!quote) { quote=*s; continue; }
846                                 }
847                         if (*s=='\\'&&s[1]) s++;
848                         *d++=*s;
849                         }
850                 c=*s++; *d='\0';
851                 }
852 }
853
854 void chat_proccmd(const char *who,char *buf)
855 {
856 char *s,*cmds;
857 int i;
858 const struct chat_cmdtab *cmd,*cmdt;
859
860         for (cmds=buf;*cmds&&isspace(*cmds);cmds++);
861         for (s=cmds;*s&&!isspace(*s);s++);
862         if (s==cmds) {
863                 vbputstring(chat_vbout,"104 Empty command.\n");
864                 return;
865                 }
866         chat_parsepars(s);
867         cmdt=chat_cmdtable;
868         if (!cmdt || !cmdt->name) {
869                 VB_LOG(CRIT,"chat_cmdtable not initialized, \"%s\" tried to execute \"%s\"",who,cmds);
870                 return;
871                 }
872         for (i=0;i<NELEM(chat_cmdint);) {
873                 if (cmdt->name) cmd=cmdt++;
874                 else cmd=&chat_cmdint[i++];
875                 assert(cmd->npars<=CHAT_CMD_MAXPARS);
876                 if (!strcmp(cmd->name,cmds)) break;
877                 }
878         if (i<NELEM(chat_cmdint)) {
879                 if (cmd->npars!=chat_cmdpar)
880                         vbprintf(chat_vbout,"205 %d parameter%s found, %d expected!\n",chat_cmdpar,(chat_cmdpar==1?"":"s"),cmd->npars);
881                 else {
882                         vbprintf(chat_vbout,"001 Running command: %s\n",cmds);
883                         VB_LOG(DEBUG,"%s is executing user command \"%s\"",who,cmds);
884                         (*cmd->fnc)();
885                         }
886                 }
887         else vbprintf(chat_vbout,"206 Unknown command \"%s\"!\n",cmds);
888 }
889
890 void crfilter(char *sr)
891 {
892 char *sw;
893         for (sw=sr;*sr;sr++)
894                 if (*sr!='\r') *sw++=*sr;
895         *sw='\0';
896 }
897
898 static void tzchange(const char *tz)
899 {
900 static char *otz=NULL;
901
902         if (tz) {
903                 free(otz);
904                 if ((otz=getenv("TZ"))) chk(otz=strdup(otz));
905                 setenv("TZ",tz,1);
906                 }
907         else {
908                 if (otz) {
909                         setenv("TZ",otz,1);
910                         free(otz); otz=NULL;
911                         }
912                 else unsetenv("TZ");
913                 }
914         tzset();
915 }
916
917 char *gmctime(time_t tm)
918 {
919 char *r,*s;
920
921         tzchange("GMT");
922         r=ctime(&tm);
923         tzchange(NULL);
924         if ((s=strchr(r,'\n')))
925                 *s='\0';
926         chk(r=strdup(r));
927         return(r);
928 }
929
930 unsigned char *bitshuffle(unsigned char *dst,unsigned char dstb,const unsigned char *src,unsigned char srcb,size_t bits,char lr)
931 {
932 unsigned char dstrem,*dstp,srcrem,now;
933 const unsigned char *srcp;
934
935         if (!dst) chk(dst=malloc((bits+dstb-1)/dstb));
936         dstrem=0; dstp=dst-1;
937         srcrem=0; srcp=src-1;
938         while (bits) {
939                 if (!dstrem) {
940                         *++dstp=0;
941                         dstrem=dstb;
942                         }
943                 if (!srcrem) {
944                         srcp++;
945                         srcrem=srcb;
946                         }
947                 dbg("dstrem=%u,srcrem=%u,bits=%u\n",dstrem,srcrem,bits);
948                 assert(dstrem&&srcrem);
949                 now=min(dstrem,srcrem);
950                 assert(bits>=now);
951                 dbg("1:*dstp=0x%02x,*srcp=0x%02x,dstrem=%u,srcrem=%u,now=%u\n",*dstp,*srcp,dstrem,srcrem,now);
952                 *dstp|=((*srcp>>(lr?srcrem-now:srcb-srcrem))&((1U<<now)-1))<<(lr?dstrem-now:dstb-dstrem);
953                 dstrem-=now;
954                 srcrem-=now;
955                 bits  -=now;
956                 dbg("2:*dstp=0x%02x,*srcp=0x%02x,dstrem=%u,srcrem=%u,now=%u\n",*dstp,*srcp,dstrem,srcrem,now);
957                 }
958         return(dst);
959 }
960
961 char *enbase64(char *dst,const unsigned char *src,size_t bits)
962 {
963 char *s,*d;
964
965         if (!dst) chk(dst=malloc(((bits+23)/24)*4+1));
966         d=bitshuffle(dst,6,src,8,bits,1); assert(d==dst);
967         d=dst+(bits=(bits+5)/6);
968         for (s=dst;s<d;s++) switch (*s) {
969                 case  0 ... 25: *s+='A'- 0; break;
970                 case 26 ... 51: *s+='a'-26; break;
971                 case 52 ... 61: *s+='0'-52; break;
972                 case 62       : *s ='+'   ; break;
973                 case 63       : *s ='/'   ; break;
974                 default: assert(0);
975                 }
976         while (bits++%4) *s++='=';
977         *s='\0';
978         return(dst);
979 }
980
981 unsigned char *debase64(unsigned char *dst,const char *src,size_t *dstlp)
982 {
983 size_t srcl,dstl;
984 char *src2,*sd;
985 const char *s;
986 unsigned char *d;
987
988         chk(src2=alloca(strlen(src))); /* +1 not needed */
989         for (sd=src2,s=src;*s;s++) switch (*s) {
990                 case 'A' ... 'Z': *sd++=*s-('A'- 0); break;
991                 case 'a' ... 'z': *sd++=*s-('a'-26); break;
992                 case '0' ... '9': *sd++=*s-('0'-52); break;
993                 case '+'        : *sd++=   (    62); break;
994                 case '/'        : *sd++=   (    63); break;
995                 case '='        :
996                         /* only 0, 1 or 2 '='(s) permitted */
997                         if (!s[1] || (s[1]=='=' && (!s[2] || (s[2]=='=' && !s[3]))))
998                                 goto out;
999                         /* FALLTHRU */
1000                 default: return(NULL);
1001                 }
1002 out:
1003         srcl=sd-src2; if ((srcl%4)==1) return(NULL);
1004         dstl=(srcl*6)/8;
1005         if (!dst) chk(dst=malloc(dstl));
1006         d=bitshuffle(dst,8,src2,6,dstl*8,1); assert(d==dst);
1007         if (dstlp) *dstlp=dstl;
1008         return(dst);
1009 }