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