Altmost compilable, weak symbol definitions still missing.
[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 static const struct chat_cmdtab  chat_cmdint[]={
805         {"help"    ,0,chatfn_help    ,"This help"},
806 #if 0
807         {"quit"    ,0,chatfn_quit    ,"Close connection"},
808         {"exit"    ,0,chatfn_quit    ,"Close connection"},
809 #endif
810 };
811
812 static void chatfn_help(void)
813 {
814 int i;
815 const struct chat_cmdtab *cmd,*cmdt;
816
817         vbputstring(chat_vbout,"000 Available commands:\n");
818         cmdt=chat_cmdtable;
819         for (i=0;i<NELEM(chat_cmdint);) {
820                 if (cmdt->name) cmd=cmdt++;
821                 else cmd=&chat_cmdint[i++];
822                 vbprintf(chat_vbout,"000 %-10s[%d] - %s\n",cmd->name,(int)cmd->npars,cmd->help);
823                 }
824         vbputstring(chat_vbout,"102 Help end.\n");
825 }
826
827 static inline void chat_parsepars(char *s)
828 {
829 char c,*d,quote;
830
831         chat_cmdpar=0;
832         if (*s) for (*s++='\0';*s&&isspace(*s);s++);
833         for (c=*s;c&&chat_cmdpar<CHAT_CMD_MAXPARS;chat_cmdpar++) {
834                 quote=0;
835                 for (chat_cmdpars[chat_cmdpar]=d=s;*s&&(quote||(!quote&&*s!=','));s++) {
836                         if (*s=='"'||*s=='\'') {
837                                 if (quote==*s) { quote=0; continue; }
838                                 if (!quote) { quote=*s; continue; }
839                                 }
840                         if (*s=='\\'&&s[1]) s++;
841                         *d++=*s;
842                         }
843                 c=*s++; *d='\0';
844                 }
845 }
846
847 void chat_proccmd(const char *who,char *buf)
848 {
849 char *s,*cmds;
850 int i;
851 const struct chat_cmdtab *cmd,*cmdt;
852
853         for (cmds=buf;*cmds&&isspace(*cmds);cmds++);
854         for (s=cmds;*s&&!isspace(*s);s++);
855         if (s==cmds) {
856                 vbputstring(chat_vbout,"104 Empty command.\n");
857                 return;
858                 }
859         chat_parsepars(s);
860         cmdt=chat_cmdtable;
861         for (i=0;i<NELEM(chat_cmdint);) {
862                 if (cmdt->name) cmd=cmdt++;
863                 else cmd=&chat_cmdint[i++];
864                 assert(cmd->npars<=CHAT_CMD_MAXPARS);
865                 if (!strcmp(cmd->name,cmds)) break;
866                 }
867         if (i<NELEM(chat_cmdint)) {
868                 if (cmd->npars!=chat_cmdpar)
869                         vbprintf(chat_vbout,"205 %d parameter%s found, %d expected!\n",chat_cmdpar,(chat_cmdpar==1?"":"s"),cmd->npars);
870                 else {
871                         vbprintf(chat_vbout,"001 Running command: %s\n",cmds);
872                         VB_LOG(DEBUG,"%s is executing user command \"%s\"",who,cmds);
873                         (*cmd->fnc)();
874                         }
875                 }
876         else vbprintf(chat_vbout,"206 Unknown command \"%s\"!\n",cmds);
877 }
878
879 void crfilter(char *sr)
880 {
881 char *sw;
882         for (sw=sr;*sr;sr++)
883                 if (*sr!='\r') *sw++=*sr;
884         *sw='\0';
885 }
886
887 static void tzchange(const char *tz)
888 {
889 static char *otz=NULL;
890
891         if (tz) {
892                 free(otz);
893                 if ((otz=getenv("TZ"))) chk(otz=strdup(otz));
894                 setenv("TZ",tz,1);
895                 }
896         else {
897                 if (otz) {
898                         setenv("TZ",otz,1);
899                         free(otz); otz=NULL;
900                         }
901                 else unsetenv("TZ");
902                 }
903         tzset();
904 }
905
906 char *gmctime(time_t tm)
907 {
908 char *r,*s;
909
910         tzchange("GMT");
911         r=ctime(&tm);
912         tzchange(NULL);
913         if ((s=strchr(r,'\n')))
914                 *s='\0';
915         chk(r=strdup(r));
916         return(r);
917 }
918
919 unsigned char *bitshuffle(unsigned char *dst,unsigned char dstb,const unsigned char *src,unsigned char srcb,size_t bits,char lr)
920 {
921 unsigned char dstrem,*dstp,srcrem,now;
922 const unsigned char *srcp;
923
924         if (!dst) chk(dst=malloc((bits+dstb-1)/dstb));
925         dstrem=0; dstp=dst-1;
926         srcrem=0; srcp=src-1;
927         while (bits) {
928                 if (!dstrem) {
929                         *++dstp=0;
930                         dstrem=dstb;
931                         }
932                 if (!srcrem) {
933                         srcp++;
934                         srcrem=srcb;
935                         }
936                 dbg("dstrem=%u,srcrem=%u,bits=%u\n",dstrem,srcrem,bits);
937                 assert(dstrem&&srcrem);
938                 now=min(dstrem,srcrem);
939                 assert(bits>=now);
940                 dbg("1:*dstp=0x%02x,*srcp=0x%02x,dstrem=%u,srcrem=%u,now=%u\n",*dstp,*srcp,dstrem,srcrem,now);
941                 *dstp|=((*srcp>>(lr?srcrem-now:srcb-srcrem))&((1U<<now)-1))<<(lr?dstrem-now:dstb-dstrem);
942                 dstrem-=now;
943                 srcrem-=now;
944                 bits  -=now;
945                 dbg("2:*dstp=0x%02x,*srcp=0x%02x,dstrem=%u,srcrem=%u,now=%u\n",*dstp,*srcp,dstrem,srcrem,now);
946                 }
947         return(dst);
948 }
949
950 char *enbase64(char *dst,const unsigned char *src,size_t bits)
951 {
952 char *s,*d;
953
954         if (!dst) chk(dst=malloc(((bits+23)/24)*4+1));
955         d=bitshuffle(dst,6,src,8,bits,1); assert(d==dst);
956         d=dst+(bits=(bits+5)/6);
957         for (s=dst;s<d;s++) switch (*s) {
958                 case  0 ... 25: *s+='A'- 0; break;
959                 case 26 ... 51: *s+='a'-26; break;
960                 case 52 ... 61: *s+='0'-52; break;
961                 case 62       : *s ='+'   ; break;
962                 case 63       : *s ='/'   ; break;
963                 default: assert(0);
964                 }
965         while (bits++%4) *s++='=';
966         *s='\0';
967         return(dst);
968 }
969
970 unsigned char *debase64(unsigned char *dst,const char *src,size_t *dstlp)
971 {
972 size_t srcl,dstl;
973 char *src2,*sd;
974 const char *s;
975 unsigned char *d;
976
977         chk(src2=alloca(strlen(src))); /* +1 not needed */
978         for (sd=src2,s=src;*s;s++) switch (*s) {
979                 case 'A' ... 'Z': *sd++=*s-('A'- 0); break;
980                 case 'a' ... 'z': *sd++=*s-('a'-26); break;
981                 case '0' ... '9': *sd++=*s-('0'-52); break;
982                 case '+'        : *sd++=   (    62); break;
983                 case '/'        : *sd++=   (    63); break;
984                 case '='        :
985                         /* only 0, 1 or 2 '='(s) permitted */
986                         if (!s[1] || (s[1]=='=' && (!s[2] || (s[2]=='=' && !s[3]))))
987                                 goto out;
988                         /* FALLTHRU */
989                 default: return(NULL);
990                 }
991 out:
992         srcl=sd-src2; if ((srcl%4)==1) return(NULL);
993         dstl=(srcl*6)/8;
994         if (!dst) chk(dst=malloc(dstl));
995         d=bitshuffle(dst,8,src2,6,dstl*8,1); assert(d==dst);
996         if (dstlp) *dstlp=dstl;
997         return(dst);
998 }