Create FUNDING.yml
[wdl/wdl-ol.git] / WDL / projectcontext.cpp
blobd710fa86eb00d6640cc57dcc911cb1675600b396
1 #ifdef _WIN32
2 #include <windows.h>
3 #else
4 #include <unistd.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #endif
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <stdarg.h>
12 #include "projectcontext.h"
14 #include "fileread.h"
15 #include "filewrite.h"
16 #include "heapbuf.h"
17 #include "wdlstring.h"
18 #include "wdlcstring.h"
19 #include "fastqueue.h"
20 #include "lineparse.h"
23 //#define WDL_MEMPROJECTCONTEXT_USE_ZLIB 1
25 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
27 #define WDL_MEMPROJECTCONTEXT_ZLIB_CHUNKSIZE 65536
28 #include "zlib/zlib.h"
30 #endif
32 #include "denormal.h"
35 char *projectcontext_fastDoubleToString(double value, char *bufOut, int prec_digits)
37 value = denormal_filter_double2(value);
39 if (value<0.0)
41 value=-value;
42 *bufOut++ = '-';
44 if (value > 2147483647.0)
46 if (value >= 1.0e40) sprintf(bufOut, "%e", value);
47 else sprintf(bufOut, "%.*f", wdl_min(prec_digits,8), value);
48 while (*bufOut) bufOut++;
49 return bufOut;
52 unsigned int value_i, frac, frac2;
53 int prec_digits2 = 0;
55 if (prec_digits>0)
57 static const unsigned int scales[9] =
59 10,
60 100,
61 1000,
62 10000,
63 100000,
64 1000000,
65 10000000,
66 100000000,
67 1000000000
70 value_i = (unsigned int)value;
71 const int value_digits =
72 value_i >= 10000 ? (
73 value_i >= 1000000 ?
74 (value_i >= 100000000 ?
75 (value_i >= 1000000000 ? 10 : 9) :
76 (value_i >= 10000000 ? 8 : 7)) :
77 (value_i >= 100000 ? 6 : 5)
81 value_i >= 100 ?
82 (value_i >= 1000 ? 4 : 3) :
83 (value_i >= 10 ? 2 : 1)
86 // double precision is limited to about 17 decimal digits of meaningful values
87 if (prec_digits + value_digits > 17) prec_digits = 17-value_digits;
89 if (prec_digits > 9)
91 prec_digits2 = prec_digits - 9;
92 prec_digits = 9;
93 if (prec_digits2 > 9) prec_digits2 = 9;
96 const unsigned int prec_scale = scales[prec_digits-1];
97 const double dfrac = (value - value_i) * prec_scale;
98 if (prec_digits2 > 0)
100 const unsigned int prec_scale2 = scales[prec_digits2-1];
101 frac = (unsigned int) dfrac;
103 double dfrac2 = (dfrac - frac) * prec_scale2;
104 frac2 = (unsigned int) (dfrac2 + 0.5);
106 const int prec_scale2_small = wdl_min(prec_scale2/1024,10);
108 if (frac2 <= prec_scale2_small) frac2=0;
109 else if (frac2 >= prec_scale2 - prec_scale2_small - 1) frac2=prec_scale2;
111 if (frac2 >= prec_scale2)
113 frac2 -= prec_scale2;
114 frac++;
117 else
119 frac = (unsigned int) (dfrac + 0.5);
120 frac2 = 0;
121 const int prec_scale_small = wdl_min(prec_scale/1024,10);
122 if (frac <= prec_scale_small) frac=0;
123 else if (frac>=prec_scale-prec_scale_small - 1) frac=prec_scale;
126 if (frac >= prec_scale) // round up to next integer
128 frac -= prec_scale;
129 value_i++;
132 else // round to int
134 value_i = (unsigned int)(value+0.5);
135 frac2 = frac = 0;
138 char digs[32];
140 if (value_i)
142 int tmp=value_i;
143 int x = 0;
144 do {
145 const int a = (tmp%10);
146 tmp/=10;
147 digs[x++]='0' + a;
148 } while (tmp);
150 while (x>0) *bufOut++ = digs[--x];
152 else
154 *bufOut++ = '0';
158 if (frac || frac2)
160 int x = 0;
161 int tmp=frac;
162 int dleft = prec_digits;
163 *bufOut++='.';
165 if (frac) do
167 const int a = (tmp%10);
168 tmp /= 10;
169 if (x || a || frac2) digs[x++] = '0'+a;
170 } while (dleft-- > 0 && tmp);
172 while (dleft-->0) *bufOut++ = '0';
173 while (x>0) *bufOut++ = digs[--x];
174 // x is 0
176 if (frac2)
178 tmp=frac2;
179 dleft = prec_digits2;
182 const int a = (tmp%10);
183 tmp /= 10;
184 if (x || a) digs[x++] = '0'+a;
185 } while (dleft-- > 0 && tmp);
187 while (dleft-->0) *bufOut++ = '0';
188 while (x>0) *bufOut++ = digs[--x];
192 *bufOut = 0;
193 return bufOut;
196 int ProjectContextFormatString(char *outbuf, size_t outbuf_size, const char *fmt, va_list va)
198 int wroffs=0;
200 while (*fmt && outbuf_size > 1)
202 char c = *fmt++;
203 if (c != '%')
205 outbuf[wroffs++] = c != '\n' ? c : ' ';
206 outbuf_size--;
207 continue;
210 if (*fmt == '%')
212 outbuf[wroffs++] = '%';
213 outbuf_size--;
214 fmt++;
215 continue;
219 const char *ofmt = fmt-1;
220 bool want_abort=false;
222 int has_prec=0;
223 int prec=0;
224 if (*fmt == '.')
226 has_prec=1;
227 fmt++;
228 while (*fmt >= '0' && *fmt <= '9') prec = prec*10 + (*fmt++-'0');
229 if (*fmt != 'f' || prec < 0 || prec>20)
231 want_abort=true;
234 else if (*fmt == '0')
236 has_prec=2;
237 fmt++;
238 while (*fmt >= '0' && *fmt <= '9') prec = prec*10 + (*fmt++-'0');
239 if (*fmt != 'x' && *fmt != 'X' && *fmt != 'd' && *fmt != 'u')
241 want_abort=true;
245 c = *fmt++;
246 if (!want_abort) switch (c)
248 case '@':
249 case 'p':
250 case 's':
252 const char *str=va_arg(va,const char *);
253 const char qc = outbuf_size >= 3 && c != 's' ? getConfigStringQuoteChar(str) : ' ';
255 if (qc != ' ')
257 outbuf[wroffs++] = qc ? qc : '`';
258 outbuf_size-=2; // will add trailing quote below
261 if (str) while (outbuf_size > 1 && *str)
263 char v = *str++;
264 if (!qc && v == '`') v = '\'';
265 outbuf[wroffs++] = v != '\n' ? v : ' ';
266 outbuf_size--;
269 if (qc != ' ')
271 outbuf[wroffs++] = qc ? qc : '`';
272 // outbuf_size already decreased above
275 break;
276 case 'c':
278 int v = (va_arg(va,int)) & 0xff;
279 outbuf[wroffs++] = v != '\n' ? v : ' ';
280 outbuf_size--;
282 break;
283 case 'd':
285 int v = va_arg(va,int);
286 if (v<0)
288 outbuf[wroffs++] = '-';
289 outbuf_size--;
290 v=-v; // this won't handle -2147483648 right, todo special case?
293 char tab[32];
294 int x=0;
297 tab[x++] = v%10;
298 v/=10;
300 while (v);
301 if (has_prec == 2) while (x<prec) { tab[x++] = 0; }
303 while (--x >= 0 && outbuf_size>1)
305 outbuf[wroffs++] = '0' + tab[x];
306 outbuf_size--;
309 break;
310 case 'u':
312 unsigned int v = va_arg(va,unsigned int);
314 char tab[32];
315 int x=0;
318 tab[x++] = v%10;
319 v/=10;
321 while (v);
322 if (has_prec == 2) while (x<prec) { tab[x++] = 0; }
324 while (--x >= 0 && outbuf_size>1)
326 outbuf[wroffs++] = '0' + tab[x];
327 outbuf_size--;
330 break;
331 case 'x':
332 case 'X':
334 const char base = (c - 'x') + 'a';
335 unsigned int v = va_arg(va,unsigned int);
337 char tab[32];
338 int x=0;
341 tab[x++] = v&0xf;
342 v>>=4;
344 while (v);
346 if (has_prec == 2) while (x<prec) { tab[x++] = 0; }
348 while (--x >= 0 && outbuf_size>1)
350 if (tab[x] < 10)
351 outbuf[wroffs++] = '0' + tab[x];
352 else
353 outbuf[wroffs++] = base + tab[x] - 10;
355 outbuf_size--;
358 break;
359 case 'f':
361 double v = va_arg(va,double);
362 if (outbuf_size<64)
364 char tmp[64];
365 projectcontext_fastDoubleToString(v,tmp,has_prec?prec:6);
366 const char *str = tmp;
367 while (outbuf_size > 1 && *str)
369 outbuf[wroffs++] = *str++;
370 outbuf_size--;
373 else
375 const char *p=projectcontext_fastDoubleToString(v,outbuf+wroffs,has_prec?prec:6);
376 int amt = (int) (p-(outbuf+wroffs));
377 wroffs += amt;
378 outbuf_size-=amt;
381 break;
382 default:
383 want_abort=true;
384 break;
386 if (want_abort)
388 #if defined(_WIN32) && defined(_DEBUG)
389 OutputDebugString("ProjectContextFormatString(): falling back to stock vsnprintf because of:");
390 OutputDebugString(ofmt);
391 #endif
392 fmt=ofmt;
393 break;
397 outbuf += wroffs;
398 outbuf[0] = 0;
399 if (outbuf_size<2||!*fmt)
400 return wroffs;
402 #if defined(_WIN32) && defined(_MSC_VER)
403 // _vsnprintf() does not always null terminate (see below)
404 _vsnprintf(outbuf,outbuf_size,fmt,va);
405 #else
406 // vsnprintf() on non-win32, always null terminates
407 vsnprintf(outbuf,outbuf_size,fmt,va);
408 #endif
410 int l;
411 outbuf_size--;
412 for (l = 0; l < outbuf_size && outbuf[l]; l ++) if (outbuf[l] == '\n') outbuf[l] = ' ';
414 #if defined(_WIN32) && defined(_MSC_VER)
415 // nul terminate for _vsnprintf()
416 outbuf[l]=0;
417 #endif
419 return wroffs+l;
424 class ProjectStateContext_Mem : public ProjectStateContext
426 public:
428 ProjectStateContext_Mem(WDL_HeapBuf *hb, int rwflags)
430 m_rwflags=rwflags;
431 m_heapbuf=hb;
432 m_pos=0;
433 m_tmpflag=0;
434 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
435 memset(&m_compstream,0,sizeof(m_compstream));
436 m_usecomp=0;
437 #endif
440 virtual ~ProjectStateContext_Mem()
442 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
443 if (m_usecomp==1)
445 FlushComp(true);
446 deflateEnd(&m_compstream);
448 else if (m_usecomp==2)
450 inflateEnd(&m_compstream);
452 #endif
455 virtual void WDL_VARARG_WARN(printf,2,3) AddLine(const char *fmt, ...);
456 virtual int GetLine(char *buf, int buflen); // returns -1 on eof
458 virtual WDL_INT64 GetOutputSize() { return m_heapbuf ? m_heapbuf->GetSize() : 0; }
460 virtual int GetTempFlag() { return m_tmpflag; }
461 virtual void SetTempFlag(int flag) { m_tmpflag=flag; }
463 int m_pos;
464 WDL_HeapBuf *m_heapbuf;
465 int m_tmpflag;
466 int m_rwflags;
468 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
469 int DecompressData()
471 if (m_pos >= m_heapbuf->GetSize()) return 1;
473 m_compstream.next_in = (unsigned char *)m_heapbuf->Get() + m_pos;
474 m_compstream.avail_in = m_heapbuf->GetSize()-m_pos;
475 m_compstream.total_in = 0;
477 int outchunk = 65536;
478 m_compstream.next_out = (unsigned char *)m_compdatabuf.Add(NULL,outchunk);
479 m_compstream.avail_out = outchunk;
480 m_compstream.total_out = 0;
482 int e = inflate(&m_compstream,Z_NO_FLUSH);
484 m_pos += m_compstream.total_in;
485 m_compdatabuf.Add(NULL,m_compstream.total_out - outchunk); // rewind
487 return e != Z_OK;
490 void FlushComp(bool eof)
492 while (m_compdatabuf.Available()>=WDL_MEMPROJECTCONTEXT_ZLIB_CHUNKSIZE || eof)
494 if (!m_heapbuf->GetSize()) m_heapbuf->SetGranul(256*1024);
495 m_compstream.next_in = (unsigned char *)m_compdatabuf.Get();
496 m_compstream.avail_in = m_compdatabuf.Available();
497 m_compstream.total_in = 0;
499 int osz = m_heapbuf->GetSize();
501 int newsz=osz + wdl_max(m_compstream.avail_in,8192) + 256;
502 m_compstream.next_out = (unsigned char *)m_heapbuf->Resize(newsz, false) + osz;
503 if (m_heapbuf->GetSize()!=newsz) return; // ERROR
504 m_compstream.avail_out = newsz-osz;
505 m_compstream.total_out=0;
507 deflate(&m_compstream,eof?Z_SYNC_FLUSH:Z_NO_FLUSH);
509 m_heapbuf->Resize(osz+m_compstream.total_out,false);
510 m_compdatabuf.Advance(m_compstream.total_in);
511 if (m_compstream.avail_out) break; // no need to process anymore
514 m_compdatabuf.Compact();
517 // these will be used for either decompression or compression, fear
518 int m_usecomp; // 0=?, -1 = fail, 1=yes
519 WDL_Queue m_compdatabuf;
520 z_stream m_compstream;
521 #endif
525 // returns length, modifies ptr to point to tmp if newline needed to be filtered
526 static int filter_newline_buf(const char **ptr, char *tmp, int tmpsz)
528 const char *use_buf = *ptr;
529 if (!use_buf) return -1;
531 int l;
532 for (l=0; use_buf[l] && use_buf[l] != '\n'; l++);
534 if (!use_buf[l]) return l;
536 lstrcpyn_safe(tmp,use_buf,tmpsz);
537 *ptr=tmp;
539 if (l >= tmpsz) return tmpsz-1;
541 for (;tmp[l]; l++) if (tmp[l] == '\n') tmp[l] = ' '; // replace any newlines with spaces
542 return l;
546 void ProjectStateContext_Mem::AddLine(const char *fmt, ...)
548 if (!m_heapbuf || !(m_rwflags&2)) return;
550 char tmp[8192];
552 const char *use_buf;
553 int l;
555 va_list va;
556 va_start(va,fmt);
558 if (fmt && fmt[0] == '%' && (fmt[1] == 's' || fmt[1] == 'S') && !fmt[2])
560 use_buf = va_arg(va,const char *);
561 l = filter_newline_buf(&use_buf,tmp,(int)sizeof(tmp)) + 1;
563 else
565 l = ProjectContextFormatString(tmp,sizeof(tmp),fmt, va) + 1;
566 use_buf = tmp;
568 va_end(va);
570 if (l < 1) return;
573 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
574 if (!m_usecomp)
576 if (deflateInit(&m_compstream,WDL_MEMPROJECTCONTEXT_USE_ZLIB)==Z_OK) m_usecomp=1;
577 else m_usecomp=-1;
580 if (m_usecomp==1)
582 m_compdatabuf.Add(use_buf,(int)l);
583 FlushComp(false);
584 return;
586 #endif
589 const int sz=m_heapbuf->GetSize();
590 if (!sz && m_heapbuf->GetGranul() < 256*1024)
592 m_heapbuf->SetGranul(256*1024);
595 char *p = (char *)m_heapbuf->ResizeOK(sz+l);
596 if (!p)
598 // ERROR, resize to 0 and return
599 m_heapbuf->Resize(0);
600 m_heapbuf=0;
601 return;
603 memcpy(p+sz,use_buf,l);
606 int ProjectStateContext_Mem::GetLine(char *buf, int buflen) // returns -1 on eof
608 if (!m_heapbuf || !(m_rwflags&1)) return -1;
610 buf[0]=0;
613 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
614 if (!m_usecomp)
616 unsigned char hdr[]={0x78,0x01};
617 if (m_heapbuf->GetSize()>2 && !memcmp(hdr,m_heapbuf->Get(),4) && inflateInit(&m_compstream)==Z_OK) m_usecomp=2;
618 else m_usecomp=-1;
620 if (m_usecomp==2)
622 int x=0;
623 for (;;)
625 const char *p = (const char*) m_compdatabuf.Get();
626 for (x = 0; x < m_compdatabuf.Available() && p[x] && p[x] != '\r' && p[x] != '\n'; x ++);
627 while (x >= m_compdatabuf.Available())
629 int err = DecompressData();
630 p = (const char *)m_compdatabuf.Get();
631 for (; x < m_compdatabuf.Available() && p[x] && p[x] != '\r' && p[x] != '\n'; x ++);
633 if (err) break;
636 if (x||!m_compdatabuf.Available()) break;
638 m_compdatabuf.Advance(1); // skip over nul or newline char
641 if (!x) return -1;
643 if (buflen > 0 && buf)
645 int l = wdl_min(buflen-1,x);
646 memcpy(buf,m_compdatabuf.Get(),l);
647 buf[l]=0;
650 m_compdatabuf.Advance(x+1);
651 m_compdatabuf.Compact();
652 return 0;
654 #endif
657 int avail = m_heapbuf->GetSize() - m_pos;
658 const char *p=(const char *)m_heapbuf->Get() + m_pos;
659 while (avail > 0 && (p[0] =='\r'||p[0]=='\n'||!p[0]||p[0] == ' ' || p[0] == '\t'))
661 p++;
662 m_pos++;
663 avail--;
665 if (avail <= 0) return -1;
667 int x;
668 for (x = 0; x < avail && p[x] && p[x] != '\n'; x ++);
669 m_pos += x+1;
671 if (buflen > 0&&buf)
673 int l = buflen-1;
674 if (l>x) l=x;
675 memcpy(buf,p,l);
676 if (l>0 && buf[l-1]=='\r') l--;
677 buf[l]=0;
679 return 0;
682 class ProjectStateContext_File : public ProjectStateContext
684 public:
686 ProjectStateContext_File(WDL_FileRead *rd, WDL_FileWrite *wr)
688 m_rd=rd;
689 m_wr=wr;
690 m_indent=0;
691 m_bytesOut=0;
692 m_errcnt=false;
693 m_tmpflag=0;
694 _rdbuf_pos = _rdbuf_valid = 0;
696 virtual ~ProjectStateContext_File(){ delete m_rd; delete m_wr; };
698 virtual void WDL_VARARG_WARN(printf,2,3) AddLine(const char *fmt, ...);
699 virtual int GetLine(char *buf, int buflen); // returns -1 on eof
701 virtual WDL_INT64 GetOutputSize() { return m_bytesOut; }
703 virtual int GetTempFlag() { return m_tmpflag; }
704 virtual void SetTempFlag(int flag) { m_tmpflag=flag; }
706 bool HasError() { return m_errcnt; }
708 WDL_INT64 m_bytesOut WDL_FIXALIGN;
710 WDL_FileRead *m_rd;
711 WDL_FileWrite *m_wr;
713 char rdbuf[4096];
714 int _rdbuf_pos, _rdbuf_valid;
716 int m_indent;
717 int m_tmpflag;
718 bool m_errcnt;
722 int ProjectStateContext_File::GetLine(char *buf, int buflen)
724 if (!m_rd||buflen<3) return -1;
726 char * const buf_orig=buf;
727 int rdpos = _rdbuf_pos;
728 int rdvalid = _rdbuf_valid;
729 buflen -= 2;
731 for (;;)
733 while (rdpos < rdvalid)
735 char c=rdbuf[rdpos++];
736 switch (c)
738 case ' ': case '\r': case '\n': case '\t': break;
739 default:
740 *buf++=c;
744 int mxl = rdvalid - rdpos;
745 if (mxl > buflen) mxl=buflen;
746 while (mxl-->0)
748 char c2 = rdbuf[rdpos++];
749 if (c2=='\n') goto finished;
751 *buf++ = c2;
752 buflen--;
754 if (rdpos>=rdvalid)
756 rdpos = 0;
757 rdvalid = m_rd->Read(rdbuf, sizeof(rdbuf));
758 if (rdvalid<1) break;
761 while (buflen > 0);
763 finished:
764 _rdbuf_pos=rdpos;
765 _rdbuf_valid=rdvalid;
767 if (buf > buf_orig && buf[-1] == '\r') buf--;
768 *buf=0;
769 return 0;
773 rdpos = 0;
774 rdvalid = m_rd->Read(rdbuf, sizeof(rdbuf));
775 if (rdvalid<1)
777 buf[0]=0;
778 return -1;
783 void ProjectStateContext_File::AddLine(const char *fmt, ...)
785 if (m_wr && !m_errcnt)
787 int err=0;
789 char tmp[8192];
790 const char *use_buf;
791 va_list va;
792 va_start(va,fmt);
793 int l;
795 if (fmt && fmt[0] == '%' && (fmt[1] == 's' || fmt[1] == 'S') && !fmt[2])
797 // special case "%s" passed, directly use it
798 use_buf = va_arg(va,const char *);
799 l = filter_newline_buf(&use_buf,tmp,(int)sizeof(tmp));
801 else
803 l = ProjectContextFormatString(tmp,sizeof(tmp),fmt, va);
804 use_buf = tmp;
807 va_end(va);
808 if (l < 0) return;
811 int a=m_indent;
812 if (use_buf[0] == '<') m_indent+=2;
813 else if (use_buf[0] == '>') a=(m_indent-=2);
815 if (a>0)
817 m_bytesOut+=a;
818 char tb[128];
819 memset(tb,' ',a < (int)sizeof(tb) ? a : (int)sizeof(tb));
820 while (a>0)
822 const int tl = a < (int)sizeof(tb) ? a : (int)sizeof(tb);
823 a-=tl;
824 m_wr->Write(tb,tl);
828 err |= m_wr->Write(use_buf,l) != l;
829 err |= m_wr->Write("\r\n",2) != 2;
830 m_bytesOut += 2 + l;
832 if (err) m_errcnt=true;
838 ProjectStateContext *ProjectCreateFileRead(const char *fn)
840 WDL_FileRead *rd = new WDL_FileRead(fn,0,65536,1);
841 if (!rd || !rd->IsOpen())
843 delete rd;
844 return NULL;
846 return new ProjectStateContext_File(rd,NULL);
848 ProjectStateContext *ProjectCreateFileWrite(const char *fn)
850 WDL_FileWrite *wr = new WDL_FileWrite(fn);
851 if (!wr || !wr->IsOpen())
853 delete wr;
854 return NULL;
856 return new ProjectStateContext_File(NULL,wr);
860 ProjectStateContext *ProjectCreateMemCtx(WDL_HeapBuf *hb)
862 return new ProjectStateContext_Mem(hb,3);
865 ProjectStateContext *ProjectCreateMemCtx_Read(const WDL_HeapBuf *hb)
867 return new ProjectStateContext_Mem((WDL_HeapBuf *)hb,1);
869 ProjectStateContext *ProjectCreateMemCtx_Write(WDL_HeapBuf *hb)
871 return new ProjectStateContext_Mem(hb,2);
877 class ProjectStateContext_FastQueue : public ProjectStateContext
879 public:
881 ProjectStateContext_FastQueue(WDL_FastQueue *fq)
883 m_fq = fq;
884 m_tmpflag=0;
887 virtual ~ProjectStateContext_FastQueue()
891 virtual void WDL_VARARG_WARN(printf,2,3) AddLine(const char *fmt, ...);
892 virtual int GetLine(char *buf, int buflen) { return -1; }//unsup
894 virtual WDL_INT64 GetOutputSize() { return m_fq ? m_fq->Available() : 0; }
896 virtual int GetTempFlag() { return m_tmpflag; }
897 virtual void SetTempFlag(int flag) { m_tmpflag=flag; }
899 WDL_FastQueue *m_fq;
900 int m_tmpflag;
905 void ProjectStateContext_FastQueue::AddLine(const char *fmt, ...)
907 if (!m_fq) return;
909 va_list va;
910 va_start(va,fmt);
912 char tmp[8192];
913 if (fmt && fmt[0] == '%' && (fmt[1] == 's' || fmt[1] == 'S') && !fmt[2])
915 const char *use_buf = va_arg(va,const char *);
916 const int l = filter_newline_buf(&use_buf,tmp,(int)sizeof(tmp));
917 if (use_buf) m_fq->Add(use_buf, l + 1);
919 else
921 const int l = ProjectContextFormatString(tmp,sizeof(tmp),fmt, va);
922 if (l>0) m_fq->Add(tmp, l+1);
924 va_end(va);
933 ProjectStateContext *ProjectCreateMemWriteFastQueue(WDL_FastQueue *fq) // only write!
935 return new ProjectStateContext_FastQueue(fq);
938 bool ProjectContext_GetNextLine(ProjectStateContext *ctx, LineParser *lpOut)
940 for (;;)
942 char linebuf[4096];
943 if (ctx->GetLine(linebuf,sizeof(linebuf)))
945 lpOut->parse("");
946 return false;
949 if (lpOut->parse(linebuf)||lpOut->getnumtokens()<=0) continue;
951 return true; // success!
957 bool ProjectContext_EatCurrentBlock(ProjectStateContext *ctx, ProjectStateContext *ctxOut)
959 int child_count=1;
960 if (ctx) for (;;)
962 char linebuf[4096];
963 if (ctx->GetLine(linebuf,sizeof(linebuf))) break;
964 const char *sp = linebuf;
965 while (*sp == ' ' || *sp == '\t') sp++;
967 const char *p = sp;
968 if (*p == '\'' || *p == '"' || *p == '`') p++; // skip a quote if any
969 if (p[0] == '>') if (--child_count < 1) return true;
971 if (ctxOut) ctxOut->AddLine("%s",sp);
973 if (p[0] == '<') child_count++;
976 return false;
980 #include "wdl_base64.h"
982 int cfg_decode_binary(ProjectStateContext *ctx, WDL_HeapBuf *hb) // 0 on success, doesnt clear hb
984 int child_count=1;
985 for (;;)
987 char linebuf[4096];
988 if (ctx->GetLine(linebuf,sizeof(linebuf))) break;
990 const char *p = linebuf;
991 while (*p == ' ' || *p == '\t') p++;
992 if (*p == '\'' || *p == '"' || *p == '`') p++; // skip a quote if any
994 if (p[0] == '<') child_count++;
995 else if (p[0] == '>') { if (child_count-- == 1) return 0; }
996 else if (child_count == 1 && p[0])
998 unsigned char buf[3200];
999 const int buf_l=wdl_base64decode(p,buf,sizeof(buf));
1000 if (buf_l)
1002 const int os=hb->GetSize();
1003 char *dest = (char*)hb->ResizeOK(os+buf_l);
1004 if (dest) memcpy(dest+os,buf,buf_l);
1008 return -1;
1011 void cfg_encode_binary(ProjectStateContext *ctx, const void *ptr, int len)
1013 if (!ctx || len < 1) return;
1015 const unsigned char *p=(const unsigned char *)ptr;
1016 if (len > 128 && len < (1<<30))
1018 // we could (probably should) use dynamic_cast<> here, but as we span modules this
1019 // raises all kinds of questions (especially with VC having the option to disable RTTI).
1020 // for now, we assume that the first void * in an object is the vtable pointer. with
1021 // testing, of course.
1022 WDL_FastQueue *fq = NULL;
1023 WDL_HeapBuf *hb = NULL;
1024 #ifndef WDL_MEMPROJECTCONTEXT_USE_ZLIB
1025 static const ProjectStateContext_Mem hb_def(NULL,0);
1026 #endif
1027 static const ProjectStateContext_FastQueue fq_def(NULL);
1028 if (*(void **)ctx == *(void **)&fq_def)
1030 fq=((ProjectStateContext_FastQueue*)ctx)->m_fq;
1032 #ifndef WDL_MEMPROJECTCONTEXT_USE_ZLIB
1033 else if (*(void **)ctx == *(void **)&hb_def)
1035 hb=((ProjectStateContext_Mem*)ctx)->m_heapbuf;
1037 #endif
1039 if (fq||hb)
1041 const int linelen8 = 280/8;
1043 const int enc_len = ((len+2)/3)*4; // every 3 characters end up as 4
1044 const int lines = (enc_len + linelen8*8 - 1) / (linelen8*8);
1046 char *wr = NULL;
1047 if (fq)
1049 wr = (char*)fq->Add(WDL_FASTQUEUE_ADD_NOZEROBUF,enc_len + lines);
1051 else if (hb)
1053 const int oldsz=hb->GetSize();
1054 wr=(char*)hb->ResizeOK(oldsz + enc_len + lines,false);
1055 if (wr) wr+=oldsz;
1058 if (wr)
1060 #ifdef _DEBUG
1061 char * const wr_end=wr + enc_len + lines;
1062 #endif
1064 int lpos = 0;
1066 while (len >= 6)
1068 const int accum = (p[0] << 16) + (p[1] << 8) + p[2];
1069 const int accum2 = (p[3] << 16) + (p[4] << 8) + p[5];
1070 wr[0] = wdl_base64_alphabet[(accum >> 18) & 0x3F];
1071 wr[1] = wdl_base64_alphabet[(accum >> 12) & 0x3F];
1072 wr[2] = wdl_base64_alphabet[(accum >> 6) & 0x3F];
1073 wr[3] = wdl_base64_alphabet[accum & 0x3F];
1074 wr[4] = wdl_base64_alphabet[(accum2 >> 18) & 0x3F];
1075 wr[5] = wdl_base64_alphabet[(accum2 >> 12) & 0x3F];
1076 wr[6] = wdl_base64_alphabet[(accum2 >> 6) & 0x3F];
1077 wr[7] = wdl_base64_alphabet[accum2 & 0x3F];
1078 wr+=8;
1079 p+=6;
1080 len-=6;
1082 if (++lpos >= linelen8) { *wr++= 0; lpos=0; }
1085 if (len >= 3)
1087 const int accum = (p[0] << 16) + (p[1] << 8) + p[2];
1088 wr[0] = wdl_base64_alphabet[(accum >> 18) & 0x3F];
1089 wr[1] = wdl_base64_alphabet[(accum >> 12) & 0x3F];
1090 wr[2] = wdl_base64_alphabet[(accum >> 6) & 0x3F];
1091 wr[3] = wdl_base64_alphabet[accum & 0x3F];
1092 wr+=4;
1093 p+=3;
1094 len-=3;
1095 lpos+=3;
1098 if (len>0)
1100 lpos += len;
1101 if (len == 2)
1103 const int accum = (p[0] << 8) | p[1];
1104 wr[0] = wdl_base64_alphabet[(accum >> 10) & 0x3F];
1105 wr[1] = wdl_base64_alphabet[(accum >> 4) & 0x3F];
1106 wr[2] = wdl_base64_alphabet[(accum & 0xF)<<2];
1108 else
1110 const int accum = p[0];
1111 wr[0] = wdl_base64_alphabet[(accum >> 2) & 0x3F];
1112 wr[1] = wdl_base64_alphabet[(accum & 0x3)<<4];
1113 wr[2] = '=';
1115 wr[3] = '=';
1116 wr+=4;
1118 if (lpos>0) *wr++=0;
1120 #ifdef _DEBUG
1121 #ifdef _WIN32
1122 if (wr != wr_end) OutputDebugString("cfg_encode_binary: block mode size mismatch!\n");
1123 #else
1124 if (wr != wr_end) printf("cfg_encode_binary: block mode size mismatch %d!\n", (int)(wr-wr_end));
1125 #endif
1126 #endif
1127 return;
1134 char buf[256];
1135 int thiss=len;
1136 if (thiss > 96) thiss=96;
1137 wdl_base64encode(p,buf,thiss);
1139 ctx->AddLine("%s",buf);
1140 p+=thiss;
1141 len-=thiss;
1143 while (len>0);
1148 int cfg_decode_textblock(ProjectStateContext *ctx, WDL_String *str) // 0 on success, appends to str
1150 int child_count=1;
1151 bool did_firstline=!!str->Get()[0];
1152 for (;;)
1154 char linebuf[4096];
1155 if (ctx->GetLine(linebuf,sizeof(linebuf))) break;
1157 const char *p = linebuf;
1158 while (*p == ' ' || *p == '\t') p++;
1159 if (*p == '\'' || *p == '"' || *p == '`') p++; // skip a quote if any
1161 if (!p[0]) continue;
1162 else if (p[0] == '<') child_count++;
1163 else if (p[0] == '>') { if (child_count-- == 1) return 0; }
1164 else if (child_count == 1 && p[0] == '|')
1166 if (!did_firstline) did_firstline=true;
1167 else str->Append("\r\n");
1168 str->Append(++p);
1171 return -1;
1174 int cfg_decode_textblock(ProjectStateContext *ctx, WDL_FastString *str) // 0 on success, appends to str
1176 int child_count=1;
1177 bool did_firstline=!!str->Get()[0];
1178 for (;;)
1180 char linebuf[4096];
1181 if (ctx->GetLine(linebuf,sizeof(linebuf))) break;
1183 const char *p = linebuf;
1184 while (*p == ' ' || *p == '\t') p++;
1185 if (*p == '\'' || *p == '"' || *p == '`') p++; // skip a quote if any
1187 if (!p[0]) continue;
1188 else if (p[0] == '<') child_count++;
1189 else if (p[0] == '>') { if (child_count-- == 1) return 0; }
1190 else if (child_count == 1 && p[0] == '|')
1192 if (!did_firstline) did_firstline=true;
1193 else str->Append("\r\n");
1194 str->Append(++p);
1197 return -1;
1202 void cfg_encode_textblock(ProjectStateContext *ctx, const char *text)
1204 WDL_String tmpcopy(text);
1205 char *txt=(char*)tmpcopy.Get();
1206 while (*txt)
1208 char *ntext=txt;
1209 while (*ntext && *ntext != '\r' && *ntext != '\n') ntext++;
1210 if (ntext > txt || *ntext)
1212 char ov=*ntext;
1213 *ntext=0;
1214 ctx->AddLine("|%s",txt);
1215 *ntext=ov;
1217 txt=ntext;
1218 if (*txt == '\r')
1220 if (*++txt== '\n') txt++;
1222 else if (*txt == '\n')
1224 if (*++txt == '\r') txt++;
1229 char getConfigStringQuoteChar(const char *p)
1231 if (!p || !*p) return '"';
1233 char fc = *p;
1234 int flags=0;
1235 while (*p && flags!=15)
1237 char c=*p++;
1238 if (c=='"') flags|=1;
1239 else if (c=='\'') flags|=2;
1240 else if (c=='`') flags|=4;
1241 else if (c == ' ' || c == '\t') flags |= 8;
1243 #ifndef PROJECTCONTEXT_USE_QUOTES_WHEN_NO_SPACES
1244 if (!(flags & 8) && fc != '"' && fc != '\'' && fc != '`' && fc != '#' && fc != ';') return ' ';
1245 #endif
1247 if (!(flags & 1)) return '"';
1248 if (!(flags & 2)) return '\'';
1249 if (!(flags & 4)) return '`';
1250 return 0;
1253 void makeEscapedConfigString(const char *in, WDL_String *out)
1255 char c;
1256 if (!in || !*in) out->Set("\"\"");
1257 else if ((c = getConfigStringQuoteChar(in)))
1259 if (c == ' ')
1261 out->Set(in);
1263 else
1265 out->Set(&c,1);
1266 out->Append(in);
1267 out->Append(&c,1);
1270 else // ick, change ` into '
1272 out->Set("`");
1273 out->Append(in);
1274 out->Append("`");
1275 char *p=out->Get()+1;
1276 while (*p && p[1])
1278 if (*p == '`') *p='\'';
1279 p++;
1284 void makeEscapedConfigString(const char *in, WDL_FastString *out)
1286 char c;
1287 if (!in || !*in) out->Set("\"\"");
1288 else if ((c = getConfigStringQuoteChar(in)))
1290 if (c == ' ')
1292 out->Set(in);
1294 else
1296 out->Set(&c,1);
1297 out->Append(in);
1298 out->Append(&c,1);
1301 else // ick, change ` into '
1303 out->Set("`");
1304 out->Append(in);
1305 out->Append("`");
1306 char *p=(char *)out->Get()+1;
1307 while (*p && p[1])
1309 if (*p == '`') *p='\'';
1310 p++;