12 #include "projectcontext.h"
15 #include "filewrite.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"
35 char *projectcontext_fastDoubleToString(double value
, char *bufOut
, int prec_digits
)
37 value
= denormal_filter_double2(value
);
52 if (value
> 2147483647.0)
54 if (value
>= 1.0e40
) sprintf(bufOut
, "%e", value
);
55 else sprintf(bufOut
, "%.*f", wdl_min(prec_digits
,8), value
);
56 while (*bufOut
) bufOut
++;
60 unsigned int value_i
, frac
, frac2
;
65 static const unsigned int scales
[9] =
78 value_i
= (unsigned int)value
;
79 const int value_digits
=
82 (value_i
>= 100000000 ?
83 (value_i
>= 1000000000 ? 10 : 9) :
84 (value_i
>= 10000000 ? 8 : 7)) :
85 (value_i
>= 100000 ? 6 : 5)
90 (value_i
>= 1000 ? 4 : 3) :
91 (value_i
>= 10 ? 2 : 1)
94 // double precision is limited to about 17 decimal digits of meaningful values
95 if (prec_digits
+ value_digits
> 17) prec_digits
= 17-value_digits
;
99 prec_digits2
= prec_digits
- 9;
101 if (prec_digits2
> 9) prec_digits2
= 9;
104 const unsigned int prec_scale
= scales
[prec_digits
-1];
105 const double dfrac
= (value
- value_i
) * prec_scale
;
106 if (prec_digits2
> 0)
108 const unsigned int prec_scale2
= scales
[prec_digits2
-1];
109 frac
= (unsigned int) dfrac
;
111 double dfrac2
= (dfrac
- frac
) * prec_scale2
;
112 frac2
= (unsigned int) (dfrac2
+ 0.5);
114 const int prec_scale2_small
= wdl_min(prec_scale2
/1024,10);
116 if (frac2
<= prec_scale2_small
) frac2
=0;
117 else if (frac2
>= prec_scale2
- prec_scale2_small
- 1) frac2
=prec_scale2
;
119 if (frac2
>= prec_scale2
)
121 frac2
-= prec_scale2
;
127 frac
= (unsigned int) (dfrac
+ 0.5);
129 const int prec_scale_small
= wdl_min(prec_scale
/1024,10);
130 if (frac
<= prec_scale_small
) frac
=0;
131 else if (frac
>=prec_scale
-prec_scale_small
- 1) frac
=prec_scale
;
134 if (frac
>= prec_scale
) // round up to next integer
142 value_i
= (unsigned int)(value
+0.5);
153 const int a
= (tmp
%10);
158 while (x
>0) *bufOut
++ = digs
[--x
];
170 int dleft
= prec_digits
;
175 const int a
= (tmp
%10);
177 if (x
|| a
|| frac2
) digs
[x
++] = '0'+a
;
178 } while (dleft
-- > 0 && tmp
);
180 while (dleft
-->0) *bufOut
++ = '0';
181 while (x
>0) *bufOut
++ = digs
[--x
];
187 dleft
= prec_digits2
;
190 const int a
= (tmp
%10);
192 if (x
|| a
) digs
[x
++] = '0'+a
;
193 } while (dleft
-- > 0 && tmp
);
195 while (dleft
-->0) *bufOut
++ = '0';
196 while (x
>0) *bufOut
++ = digs
[--x
];
204 int ProjectContextFormatString(char *outbuf
, size_t outbuf_size
, const char *fmt
, va_list va
)
208 while (*fmt
&& outbuf_size
> 1)
213 outbuf
[wroffs
++] = c
!= '\n' ? c
: ' ';
220 outbuf
[wroffs
++] = '%';
227 const char *ofmt
= fmt
-1;
228 bool want_abort
=false;
236 while (*fmt
>= '0' && *fmt
<= '9') prec
= prec
*10 + (*fmt
++-'0');
237 if (*fmt
!= 'f' || prec
< 0 || prec
>20)
242 else if (*fmt
== '0')
246 while (*fmt
>= '0' && *fmt
<= '9') prec
= prec
*10 + (*fmt
++-'0');
247 if (*fmt
!= 'x' && *fmt
!= 'X' && *fmt
!= 'd' && *fmt
!= 'u')
254 if (!want_abort
) switch (c
)
260 const char *str
=va_arg(va
,const char *);
261 bool prefer_quoteless
= true;
262 if (c
== 'p' && *fmt
== '~') // %p~ to bias towards "string" (legacy)
264 prefer_quoteless
= false;
267 const char qc
= outbuf_size
>= 3 && c
!= 's' ? getConfigStringQuoteChar(str
, prefer_quoteless
) : ' ';
271 outbuf
[wroffs
++] = qc
? qc
: '`';
272 outbuf_size
-=2; // will add trailing quote below
275 if (str
) while (outbuf_size
> 1 && *str
)
278 if (!qc
&& v
== '`') v
= '\'';
279 outbuf
[wroffs
++] = v
!= '\n' && v
!= '\r' ? v
: ' ';
285 outbuf
[wroffs
++] = qc
? qc
: '`';
286 // outbuf_size already decreased above
292 int v
= (va_arg(va
,int)) & 0xff;
293 outbuf
[wroffs
++] = v
!= '\n' ? v
: ' ';
299 int v
= va_arg(va
,int);
302 outbuf
[wroffs
++] = '-';
304 v
=-v
; // this won't handle -2147483648 right, todo special case?
315 if (has_prec
== 2) while (x
<prec
) { tab
[x
++] = 0; }
317 while (--x
>= 0 && outbuf_size
>1)
319 outbuf
[wroffs
++] = '0' + tab
[x
];
326 unsigned int v
= va_arg(va
,unsigned int);
336 if (has_prec
== 2) while (x
<prec
) { tab
[x
++] = 0; }
338 while (--x
>= 0 && outbuf_size
>1)
340 outbuf
[wroffs
++] = '0' + tab
[x
];
348 const char base
= (c
- 'x') + 'a';
349 unsigned int v
= va_arg(va
,unsigned int);
360 if (has_prec
== 2) while (x
<prec
) { tab
[x
++] = 0; }
362 while (--x
>= 0 && outbuf_size
>1)
365 outbuf
[wroffs
++] = '0' + tab
[x
];
367 outbuf
[wroffs
++] = base
+ tab
[x
] - 10;
375 double v
= va_arg(va
,double);
379 projectcontext_fastDoubleToString(v
,tmp
,has_prec
?prec
:6);
380 const char *str
= tmp
;
381 while (outbuf_size
> 1 && *str
)
383 outbuf
[wroffs
++] = *str
++;
389 const char *p
=projectcontext_fastDoubleToString(v
,outbuf
+wroffs
,has_prec
?prec
:6);
390 int amt
= (int) (p
-(outbuf
+wroffs
));
409 if (outbuf_size
<2||!*fmt
)
412 #if defined(_WIN32) && defined(_MSC_VER)
413 // _vsnprintf() does not always null terminate (see below)
414 _vsnprintf(outbuf
,outbuf_size
,fmt
,va
);
416 // vsnprintf() on non-win32, always null terminates
417 vsnprintf(outbuf
,outbuf_size
,fmt
,va
);
422 for (l
= 0; l
< outbuf_size
&& outbuf
[l
]; l
++) if (outbuf
[l
] == '\n') outbuf
[l
] = ' ';
424 #if defined(_WIN32) && defined(_MSC_VER)
425 // nul terminate for _vsnprintf()
434 class ProjectStateContext_Mem
: public ProjectStateContext
438 ProjectStateContext_Mem(WDL_HeapBuf
*hb
, int rwflags
)
444 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
445 memset(&m_compstream
,0,sizeof(m_compstream
));
450 virtual ~ProjectStateContext_Mem()
452 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
456 deflateEnd(&m_compstream
);
458 else if (m_usecomp
==2)
460 inflateEnd(&m_compstream
);
465 virtual void WDL_VARARG_WARN(printf
,2,3) AddLine(const char *fmt
, ...);
466 virtual int GetLine(char *buf
, int buflen
); // returns -1 on eof
468 virtual WDL_INT64
GetOutputSize() { return m_heapbuf
? m_heapbuf
->GetSize() : 0; }
470 virtual int GetTempFlag() { return m_tmpflag
; }
471 virtual void SetTempFlag(int flag
) { m_tmpflag
=flag
; }
474 WDL_HeapBuf
*m_heapbuf
;
478 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
481 if (m_pos
>= m_heapbuf
->GetSize()) return 1;
483 m_compstream
.next_in
= (unsigned char *)m_heapbuf
->Get() + m_pos
;
484 m_compstream
.avail_in
= m_heapbuf
->GetSize()-m_pos
;
485 m_compstream
.total_in
= 0;
487 int outchunk
= 65536;
488 m_compstream
.next_out
= (unsigned char *)m_compdatabuf
.Add(NULL
,outchunk
);
489 m_compstream
.avail_out
= outchunk
;
490 m_compstream
.total_out
= 0;
492 int e
= inflate(&m_compstream
,Z_NO_FLUSH
);
494 m_pos
+= m_compstream
.total_in
;
495 m_compdatabuf
.Add(NULL
,m_compstream
.total_out
- outchunk
); // rewind
500 void FlushComp(bool eof
)
502 while (m_compdatabuf
.Available()>=WDL_MEMPROJECTCONTEXT_ZLIB_CHUNKSIZE
|| eof
)
504 if (!m_heapbuf
->GetSize()) m_heapbuf
->SetGranul(256*1024);
505 m_compstream
.next_in
= (unsigned char *)m_compdatabuf
.Get();
506 m_compstream
.avail_in
= m_compdatabuf
.Available();
507 m_compstream
.total_in
= 0;
509 int osz
= m_heapbuf
->GetSize();
511 int newsz
=osz
+ wdl_max(m_compstream
.avail_in
,8192) + 256;
512 m_compstream
.next_out
= (unsigned char *)m_heapbuf
->Resize(newsz
, false) + osz
;
513 if (m_heapbuf
->GetSize()!=newsz
) return; // ERROR
514 m_compstream
.avail_out
= newsz
-osz
;
515 m_compstream
.total_out
=0;
517 deflate(&m_compstream
,eof
?Z_SYNC_FLUSH
:Z_NO_FLUSH
);
519 m_heapbuf
->Resize(osz
+m_compstream
.total_out
,false);
520 m_compdatabuf
.Advance(m_compstream
.total_in
);
521 if (m_compstream
.avail_out
) break; // no need to process anymore
524 m_compdatabuf
.Compact();
527 // these will be used for either decompression or compression, fear
528 int m_usecomp
; // 0=?, -1 = fail, 1=yes
529 WDL_Queue m_compdatabuf
;
530 z_stream m_compstream
;
535 // returns length, modifies ptr to point to tmp if newline needed to be filtered
536 static int filter_newline_buf(const char **ptr
, char *tmp
, int tmpsz
)
538 const char *use_buf
= *ptr
;
539 if (!use_buf
) return -1;
542 for (l
=0; use_buf
[l
] && use_buf
[l
] != '\n'; l
++);
544 if (!use_buf
[l
]) return l
;
546 lstrcpyn_safe(tmp
,use_buf
,tmpsz
);
549 if (l
>= tmpsz
) return tmpsz
-1;
551 for (;tmp
[l
]; l
++) if (tmp
[l
] == '\n') tmp
[l
] = ' '; // replace any newlines with spaces
556 void ProjectStateContext_Mem::AddLine(const char *fmt
, ...)
558 if (!m_heapbuf
|| !(m_rwflags
&2)) return;
568 if (fmt
&& fmt
[0] == '%' && (fmt
[1] == 's' || fmt
[1] == 'S') && !fmt
[2])
570 use_buf
= va_arg(va
,const char *);
571 l
= filter_newline_buf(&use_buf
,tmp
,(int)sizeof(tmp
)) + 1;
575 l
= ProjectContextFormatString(tmp
,sizeof(tmp
),fmt
, va
) + 1;
583 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
586 if (deflateInit(&m_compstream
,WDL_MEMPROJECTCONTEXT_USE_ZLIB
)==Z_OK
) m_usecomp
=1;
592 m_compdatabuf
.Add(use_buf
,(int)l
);
599 const int sz
=m_heapbuf
->GetSize();
600 if (!sz
&& m_heapbuf
->GetGranul() < 256*1024)
602 m_heapbuf
->SetGranul(256*1024);
605 char *p
= (char *)m_heapbuf
->ResizeOK(sz
+l
);
608 // ERROR, resize to 0 and return
609 m_heapbuf
->Resize(0);
613 memcpy(p
+sz
,use_buf
,l
);
616 int ProjectStateContext_Mem::GetLine(char *buf
, int buflen
) // returns -1 on eof
618 if (!m_heapbuf
|| !(m_rwflags
&1)) return -1;
623 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
626 unsigned char hdr
[]={0x78,0x01};
627 if (m_heapbuf
->GetSize()>2 && !memcmp(hdr
,m_heapbuf
->Get(),4) && inflateInit(&m_compstream
)==Z_OK
) m_usecomp
=2;
635 const char *p
= (const char*) m_compdatabuf
.Get();
636 for (x
= 0; x
< m_compdatabuf
.Available() && p
[x
] && p
[x
] != '\r' && p
[x
] != '\n'; x
++);
637 while (x
>= m_compdatabuf
.Available())
639 int err
= DecompressData();
640 p
= (const char *)m_compdatabuf
.Get();
641 for (; x
< m_compdatabuf
.Available() && p
[x
] && p
[x
] != '\r' && p
[x
] != '\n'; x
++);
646 if (x
||!m_compdatabuf
.Available()) break;
648 m_compdatabuf
.Advance(1); // skip over nul or newline char
653 if (buflen
> 0 && buf
)
655 int l
= wdl_min(buflen
-1,x
);
656 memcpy(buf
,m_compdatabuf
.Get(),l
);
660 m_compdatabuf
.Advance(x
+1);
661 m_compdatabuf
.Compact();
667 int avail
= m_heapbuf
->GetSize() - m_pos
;
668 const char *p
=(const char *)m_heapbuf
->Get() + m_pos
;
669 while (avail
> 0 && (p
[0] =='\r'||p
[0]=='\n'||!p
[0]||p
[0] == ' ' || p
[0] == '\t'))
675 if (avail
<= 0) return -1;
678 for (x
= 0; x
< avail
&& p
[x
] && p
[x
] != '\n'; x
++);
686 if (l
>0 && buf
[l
-1]=='\r') l
--;
692 class ProjectStateContext_File
: public ProjectStateContext
696 ProjectStateContext_File(WDL_FileRead
*rd
, WDL_FileWrite
*wr
)
704 _rdbuf_pos
= _rdbuf_valid
= 0;
706 virtual ~ProjectStateContext_File(){ delete m_rd
; delete m_wr
; };
708 virtual void WDL_VARARG_WARN(printf
,2,3) AddLine(const char *fmt
, ...);
709 virtual int GetLine(char *buf
, int buflen
); // returns -1 on eof
711 virtual WDL_INT64
GetOutputSize() { return m_bytesOut
; }
713 virtual int GetTempFlag() { return m_tmpflag
; }
714 virtual void SetTempFlag(int flag
) { m_tmpflag
=flag
; }
716 bool HasError() { return m_errcnt
; }
718 WDL_INT64 m_bytesOut WDL_FIXALIGN
;
724 int _rdbuf_pos
, _rdbuf_valid
;
732 int ProjectStateContext_File::GetLine(char *buf
, int buflen
)
734 if (!m_rd
||buflen
<3) return -1;
736 char * const buf_orig
=buf
;
737 int rdpos
= _rdbuf_pos
;
738 int rdvalid
= _rdbuf_valid
;
743 while (rdpos
< rdvalid
)
745 char c
=rdbuf
[rdpos
++];
748 case ' ': case '\r': case '\n': case '\t': break;
754 int mxl
= rdvalid
- rdpos
;
755 if (mxl
> buflen
) mxl
=buflen
;
758 char c2
= rdbuf
[rdpos
++];
759 if (c2
=='\n') goto finished
;
767 rdvalid
= m_rd
->Read(rdbuf
, sizeof(rdbuf
));
768 if (rdvalid
<1) break;
775 _rdbuf_valid
=rdvalid
;
777 if (buf
> buf_orig
&& buf
[-1] == '\r') buf
--;
784 rdvalid
= m_rd
->Read(rdbuf
, sizeof(rdbuf
));
793 void ProjectStateContext_File::AddLine(const char *fmt
, ...)
795 if (m_wr
&& !m_errcnt
)
805 if (fmt
&& fmt
[0] == '%' && (fmt
[1] == 's' || fmt
[1] == 'S') && !fmt
[2])
807 // special case "%s" passed, directly use it
808 use_buf
= va_arg(va
,const char *);
809 l
= filter_newline_buf(&use_buf
,tmp
,(int)sizeof(tmp
));
813 l
= ProjectContextFormatString(tmp
,sizeof(tmp
),fmt
, va
);
822 if (use_buf
[0] == '<') m_indent
+=2;
823 else if (use_buf
[0] == '>') a
=(m_indent
-=2);
829 memset(tb
,' ',a
< (int)sizeof(tb
) ? a
: (int)sizeof(tb
));
832 const int tl
= a
< (int)sizeof(tb
) ? a
: (int)sizeof(tb
);
838 err
|= m_wr
->Write(use_buf
,l
) != l
;
839 err
|= m_wr
->Write("\r\n",2) != 2;
842 if (err
) m_errcnt
=true;
848 ProjectStateContext
*ProjectCreateFileRead(const char *fn
)
850 WDL_FileRead
*rd
= new WDL_FileRead(fn
,0,65536,1);
851 if (!rd
|| !rd
->IsOpen())
856 return new ProjectStateContext_File(rd
,NULL
);
858 ProjectStateContext
*ProjectCreateFileWrite(const char *fn
)
860 WDL_FileWrite
*wr
= new WDL_FileWrite(fn
);
861 if (!wr
|| !wr
->IsOpen())
866 return new ProjectStateContext_File(NULL
,wr
);
870 ProjectStateContext
*ProjectCreateMemCtx(WDL_HeapBuf
*hb
)
872 return new ProjectStateContext_Mem(hb
,3);
875 ProjectStateContext
*ProjectCreateMemCtx_Read(const WDL_HeapBuf
*hb
)
877 return new ProjectStateContext_Mem((WDL_HeapBuf
*)hb
,1);
879 ProjectStateContext
*ProjectCreateMemCtx_Write(WDL_HeapBuf
*hb
)
881 return new ProjectStateContext_Mem(hb
,2);
887 class ProjectStateContext_FastQueue
: public ProjectStateContext
891 ProjectStateContext_FastQueue(WDL_FastQueue
*fq
)
897 virtual ~ProjectStateContext_FastQueue()
901 virtual void WDL_VARARG_WARN(printf
,2,3) AddLine(const char *fmt
, ...);
902 virtual int GetLine(char *buf
, int buflen
) { return -1; }//unsup
904 virtual WDL_INT64
GetOutputSize() { return m_fq
? m_fq
->Available() : 0; }
906 virtual int GetTempFlag() { return m_tmpflag
; }
907 virtual void SetTempFlag(int flag
) { m_tmpflag
=flag
; }
915 void ProjectStateContext_FastQueue::AddLine(const char *fmt
, ...)
923 if (fmt
&& fmt
[0] == '%' && (fmt
[1] == 's' || fmt
[1] == 'S') && !fmt
[2])
925 const char *use_buf
= va_arg(va
,const char *);
926 const int l
= filter_newline_buf(&use_buf
,tmp
,(int)sizeof(tmp
));
927 if (use_buf
) m_fq
->Add(use_buf
, l
+ 1);
931 const int l
= ProjectContextFormatString(tmp
,sizeof(tmp
),fmt
, va
);
932 if (l
>0) m_fq
->Add(tmp
, l
+1);
943 ProjectStateContext
*ProjectCreateMemWriteFastQueue(WDL_FastQueue
*fq
) // only write!
945 return new ProjectStateContext_FastQueue(fq
);
948 bool ProjectContext_GetNextLine(ProjectStateContext
*ctx
, LineParser
*lpOut
)
953 if (ctx
->GetLine(linebuf
,sizeof(linebuf
)))
959 if (lpOut
->parse(linebuf
)||lpOut
->getnumtokens()<=0) continue;
961 return true; // success!
967 bool ProjectContext_EatCurrentBlock(ProjectStateContext
*ctx
, ProjectStateContext
*ctxOut
)
973 if (ctx
->GetLine(linebuf
,sizeof(linebuf
))) break;
974 const char *sp
= linebuf
;
975 while (*sp
== ' ' || *sp
== '\t') sp
++;
978 if (*p
== '\'' || *p
== '"' || *p
== '`') p
++; // skip a quote if any
979 if (p
[0] == '>') if (--child_count
< 1) return true;
981 if (ctxOut
) ctxOut
->AddLine("%s",sp
);
983 if (p
[0] == '<') child_count
++;
990 #include "wdl_base64.h"
992 int cfg_decode_binary(ProjectStateContext
*ctx
, WDL_HeapBuf
*hb
) // 0 on success, doesnt clear hb
998 if (ctx
->GetLine(linebuf
,sizeof(linebuf
))) break;
1000 const char *p
= linebuf
;
1001 while (*p
== ' ' || *p
== '\t') p
++;
1002 if (*p
== '\'' || *p
== '"' || *p
== '`') p
++; // skip a quote if any
1004 if (p
[0] == '<') child_count
++;
1005 else if (p
[0] == '>') { if (child_count
-- == 1) return 0; }
1006 else if (child_count
== 1 && p
[0])
1008 unsigned char buf
[3200];
1009 const int buf_l
=wdl_base64decode(p
,buf
,sizeof(buf
));
1012 const int os
=hb
->GetSize();
1013 char *dest
= (char*)hb
->ResizeOK(os
+buf_l
);
1014 if (dest
) memcpy(dest
+os
,buf
,buf_l
);
1021 void cfg_encode_binary(ProjectStateContext
*ctx
, const void *ptr
, int len
)
1023 if (!ctx
|| len
< 1) return;
1025 const unsigned char *p
=(const unsigned char *)ptr
;
1026 if (len
> 128 && len
< (1<<30))
1028 // we could (probably should) use dynamic_cast<> here, but as we span modules this
1029 // raises all kinds of questions (especially with VC having the option to disable RTTI).
1030 // for now, we assume that the first void * in an object is the vtable pointer. with
1031 // testing, of course.
1032 WDL_FastQueue
*fq
= NULL
;
1033 WDL_HeapBuf
*hb
= NULL
;
1034 #ifndef WDL_MEMPROJECTCONTEXT_USE_ZLIB
1035 static const ProjectStateContext_Mem
hb_def(NULL
,0);
1037 static const ProjectStateContext_FastQueue
fq_def(NULL
);
1038 if (*(void **)ctx
== *(void **)&fq_def
)
1040 fq
=((ProjectStateContext_FastQueue
*)ctx
)->m_fq
;
1042 #ifndef WDL_MEMPROJECTCONTEXT_USE_ZLIB
1043 else if (*(void **)ctx
== *(void **)&hb_def
)
1045 hb
=((ProjectStateContext_Mem
*)ctx
)->m_heapbuf
;
1051 const int linelen8
= 280/8;
1053 const int enc_len
= ((len
+2)/3)*4; // every 3 characters end up as 4
1054 const int lines
= (enc_len
+ linelen8
*8 - 1) / (linelen8
*8);
1059 wr
= (char*)fq
->Add(WDL_FASTQUEUE_ADD_NOZEROBUF
,enc_len
+ lines
);
1063 const int oldsz
=hb
->GetSize();
1064 wr
=(char*)hb
->ResizeOK(oldsz
+ enc_len
+ lines
,false);
1071 char * const wr_end
=wr
+ enc_len
+ lines
;
1078 const int accum
= (p
[0] << 16) + (p
[1] << 8) + p
[2];
1079 const int accum2
= (p
[3] << 16) + (p
[4] << 8) + p
[5];
1080 wr
[0] = wdl_base64_alphabet
[(accum
>> 18) & 0x3F];
1081 wr
[1] = wdl_base64_alphabet
[(accum
>> 12) & 0x3F];
1082 wr
[2] = wdl_base64_alphabet
[(accum
>> 6) & 0x3F];
1083 wr
[3] = wdl_base64_alphabet
[accum
& 0x3F];
1084 wr
[4] = wdl_base64_alphabet
[(accum2
>> 18) & 0x3F];
1085 wr
[5] = wdl_base64_alphabet
[(accum2
>> 12) & 0x3F];
1086 wr
[6] = wdl_base64_alphabet
[(accum2
>> 6) & 0x3F];
1087 wr
[7] = wdl_base64_alphabet
[accum2
& 0x3F];
1092 if (++lpos
>= linelen8
) { *wr
++= 0; lpos
=0; }
1097 const int accum
= (p
[0] << 16) + (p
[1] << 8) + p
[2];
1098 wr
[0] = wdl_base64_alphabet
[(accum
>> 18) & 0x3F];
1099 wr
[1] = wdl_base64_alphabet
[(accum
>> 12) & 0x3F];
1100 wr
[2] = wdl_base64_alphabet
[(accum
>> 6) & 0x3F];
1101 wr
[3] = wdl_base64_alphabet
[accum
& 0x3F];
1113 const int accum
= (p
[0] << 8) | p
[1];
1114 wr
[0] = wdl_base64_alphabet
[(accum
>> 10) & 0x3F];
1115 wr
[1] = wdl_base64_alphabet
[(accum
>> 4) & 0x3F];
1116 wr
[2] = wdl_base64_alphabet
[(accum
& 0xF)<<2];
1120 const int accum
= p
[0];
1121 wr
[0] = wdl_base64_alphabet
[(accum
>> 2) & 0x3F];
1122 wr
[1] = wdl_base64_alphabet
[(accum
& 0x3)<<4];
1128 if (lpos
>0) *wr
++=0;
1131 if (wr
!= wr_end
) wdl_log("cfg_encode_binary: block mode size mismatch %d!\n", (int)(wr
-wr_end
));
1142 if (thiss
> 96) thiss
=96;
1143 wdl_base64encode(p
,buf
,thiss
);
1145 ctx
->AddLine("%s",buf
);
1154 int cfg_decode_textblock(ProjectStateContext
*ctx
, WDL_FastString
*str
) // 0 on success, appends to str
1157 bool did_firstline
=!!str
->Get()[0];
1161 if (ctx
->GetLine(linebuf
,sizeof(linebuf
))) break;
1163 const char *p
= linebuf
;
1164 while (*p
== ' ' || *p
== '\t') p
++;
1165 if (*p
== '\'' || *p
== '"' || *p
== '`') p
++; // skip a quote if any
1167 if (!p
[0]) continue;
1168 else if (p
[0] == '<') child_count
++;
1169 else if (p
[0] == '>') { if (child_count
-- == 1) return 0; }
1170 else if (child_count
== 1)
1172 const char *prefix
= did_firstline
? "\r\n" : "";
1173 // lines can have a prefix immediately before | to specify the line ending of the previous line
1176 case 'c': prefix
=""; p
++; break;
1177 case 'n': prefix
="\n"; p
++; break;
1178 case 'r': prefix
="\r"; p
++; break;
1179 case 'R': prefix
="\n\r"; p
++; break;
1184 if (*prefix
) str
->Append(prefix
);
1194 void cfg_encode_textblock(ProjectStateContext
*ctx
, const char *txt
) // splits long lines
1199 while (txt
[l
] && l
< 4000 && txt
[l
] != '\r' && txt
[l
] != '\n') l
++;
1200 ctx
->AddLine("|%.*s",l
,txt
);
1204 if (*++txt
== '\n') txt
++;
1206 else if (*txt
== '\n')
1208 if (*++txt
== '\r') txt
++;
1213 void cfg_encode_textblock2(ProjectStateContext
*ctx
, const char *txt
) // preserves long lines, line endings
1219 while (txt
[l
] && l
< 2000 && txt
[l
] != '\r' && txt
[l
] != '\n') l
++;
1220 ctx
->AddLine("%c|%.*s",prefix
,l
,txt
);
1231 else if (*txt
== '\n')
1240 else if (*txt
) prefix
= 'c';
1244 ctx
->AddLine("%c|",prefix
);
1248 char getConfigStringQuoteChar(const char *p
, bool prefer_quoteless
)
1250 if (!p
|| !*p
) return '"';
1254 while (*p
&& flags
!=15)
1257 if (c
=='"') flags
|=1;
1258 else if (c
=='\'') flags
|=2;
1259 else if (c
=='`') flags
|=4;
1260 else if (c
== ' ' || c
== '\t' || c
== '\n' || c
== '\r') flags
|= 8;
1262 if (prefer_quoteless
|| flags
== 7)
1264 if (!(flags
& 8) && fc
!= '"' && fc
!= '\'' && fc
!= '`' && fc
!= '#' && fc
!= ';') return ' ';
1267 if (!(flags
& 1)) return '"';
1268 if (!(flags
& 2)) return '\'';
1269 if (!(flags
& 4)) return '`';
1273 bool configStringWantsBlockEncoding(const char *in
) // returns true if over 1k long, has newlines, or contains all quote chars
1275 int maxl
= 1024, flags
= 0;
1280 case 0: return false;
1281 case '\n': return true;
1282 case '"': if ((flags
|=1)==7) return true; break;
1283 case '`': if ((flags
|=2)==7) return true; break;
1284 case '\'': if ((flags
|=4)==7) return true; break;
1290 void makeEscapedConfigString(const char *in
, WDL_FastString
*out
)
1293 if (!in
|| !*in
) out
->Set("\"\"");
1294 else if ((c
= getConfigStringQuoteChar(in
)))
1307 else // ick, change ` into '
1312 char *p
=(char *)out
->Get()+1;
1315 if (*p
== '`') *p
='\'';
1316 else if (*p
== '\r' || *p
== '\n') *p
=' ';