#ifdef _WIN32 #include #else #include #include #include #endif #include #include #include #include "projectcontext.h" #include "fileread.h" #include "filewrite.h" #include "heapbuf.h" #include "wdlstring.h" #include "wdlcstring.h" #include "fastqueue.h" #include "lineparse.h" //#define WDL_MEMPROJECTCONTEXT_USE_ZLIB 1 #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB #define WDL_MEMPROJECTCONTEXT_ZLIB_CHUNKSIZE 65536 #include "zlib/zlib.h" #endif #include "denormal.h" char *projectcontext_fastDoubleToString(double value, char *bufOut, int prec_digits) { value = denormal_filter_double2(value); if (value<0.0) { value=-value; *bufOut++ = '-'; } if (value < 1e-20) { *bufOut++ = '0'; *bufOut = 0; return bufOut; } if (value > 2147483647.0) { if (value >= 1.0e40) sprintf(bufOut, "%e", value); else sprintf(bufOut, "%.*f", wdl_min(prec_digits,8), value); while (*bufOut) bufOut++; return bufOut; } unsigned int value_i, frac, frac2; int prec_digits2 = 0; if (prec_digits>0) { static const unsigned int scales[9] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; value_i = (unsigned int)value; const int value_digits = value_i >= 10000 ? ( value_i >= 1000000 ? (value_i >= 100000000 ? (value_i >= 1000000000 ? 10 : 9) : (value_i >= 10000000 ? 8 : 7)) : (value_i >= 100000 ? 6 : 5) ) : ( value_i >= 100 ? (value_i >= 1000 ? 4 : 3) : (value_i >= 10 ? 2 : 1) ); // double precision is limited to about 17 decimal digits of meaningful values if (prec_digits + value_digits > 17) prec_digits = 17-value_digits; if (prec_digits > 9) { prec_digits2 = prec_digits - 9; prec_digits = 9; if (prec_digits2 > 9) prec_digits2 = 9; } const unsigned int prec_scale = scales[prec_digits-1]; const double dfrac = (value - value_i) * prec_scale; if (prec_digits2 > 0) { const unsigned int prec_scale2 = scales[prec_digits2-1]; frac = (unsigned int) dfrac; double dfrac2 = (dfrac - frac) * prec_scale2; frac2 = (unsigned int) (dfrac2 + 0.5); const int prec_scale2_small = wdl_min(prec_scale2/1024,10); if (frac2 <= prec_scale2_small) frac2=0; else if (frac2 >= prec_scale2 - prec_scale2_small - 1) frac2=prec_scale2; if (frac2 >= prec_scale2) { frac2 -= prec_scale2; frac++; } } else { frac = (unsigned int) (dfrac + 0.5); frac2 = 0; const int prec_scale_small = wdl_min(prec_scale/1024,10); if (frac <= prec_scale_small) frac=0; else if (frac>=prec_scale-prec_scale_small - 1) frac=prec_scale; } if (frac >= prec_scale) // round up to next integer { frac -= prec_scale; value_i++; } } else // round to int { value_i = (unsigned int)(value+0.5); frac2 = frac = 0; } char digs[32]; if (value_i) { int tmp=value_i; int x = 0; do { const int a = (tmp%10); tmp/=10; digs[x++]='0' + a; } while (tmp); while (x>0) *bufOut++ = digs[--x]; } else { *bufOut++ = '0'; } if (frac || frac2) { int x = 0; int tmp=frac; int dleft = prec_digits; *bufOut++='.'; if (frac) do { const int a = (tmp%10); tmp /= 10; if (x || a || frac2) digs[x++] = '0'+a; } while (dleft-- > 0 && tmp); while (dleft-->0) *bufOut++ = '0'; while (x>0) *bufOut++ = digs[--x]; // x is 0 if (frac2) { tmp=frac2; dleft = prec_digits2; do { const int a = (tmp%10); tmp /= 10; if (x || a) digs[x++] = '0'+a; } while (dleft-- > 0 && tmp); while (dleft-->0) *bufOut++ = '0'; while (x>0) *bufOut++ = digs[--x]; } } *bufOut = 0; return bufOut; } int ProjectContextFormatString(char *outbuf, size_t outbuf_size, const char *fmt, va_list va) { int wroffs=0; while (*fmt && outbuf_size > 1) { char c = *fmt++; if (c != '%') { outbuf[wroffs++] = c != '\n' ? c : ' '; outbuf_size--; continue; } if (*fmt == '%') { outbuf[wroffs++] = '%'; outbuf_size--; fmt++; continue; } const char *ofmt = fmt-1; bool want_abort=false; int has_prec=0; int prec=0; if (*fmt == '.') { has_prec=1; fmt++; while (*fmt >= '0' && *fmt <= '9') prec = prec*10 + (*fmt++-'0'); if (*fmt != 'f' || prec < 0 || prec>20) { want_abort=true; } } else if (*fmt == '0') { has_prec=2; fmt++; while (*fmt >= '0' && *fmt <= '9') prec = prec*10 + (*fmt++-'0'); if (*fmt != 'x' && *fmt != 'X' && *fmt != 'd' && *fmt != 'u') { want_abort=true; } } c = *fmt++; if (!want_abort) switch (c) { case '@': case 'p': case 's': { const char *str=va_arg(va,const char *); bool prefer_quoteless = true; if (c == 'p' && *fmt == '~') // %p~ to bias towards "string" (legacy) { prefer_quoteless = false; fmt++; } const char qc = outbuf_size >= 3 && c != 's' ? getConfigStringQuoteChar(str, prefer_quoteless) : ' '; if (qc != ' ') { outbuf[wroffs++] = qc ? qc : '`'; outbuf_size-=2; // will add trailing quote below } if (str) while (outbuf_size > 1 && *str) { char v = *str++; if (!qc && v == '`') v = '\''; outbuf[wroffs++] = v != '\n' && v != '\r' ? v : ' '; outbuf_size--; } if (qc != ' ') { outbuf[wroffs++] = qc ? qc : '`'; // outbuf_size already decreased above } } break; case 'c': { int v = (va_arg(va,int)) & 0xff; outbuf[wroffs++] = v != '\n' ? v : ' '; outbuf_size--; } break; case 'd': { int v = va_arg(va,int); if (v<0) { outbuf[wroffs++] = '-'; outbuf_size--; v=-v; // this won't handle -2147483648 right, todo special case? } char tab[32]; int x=0; do { tab[x++] = v%10; v/=10; } while (v); if (has_prec == 2) while (x= 0 && outbuf_size>1) { outbuf[wroffs++] = '0' + tab[x]; outbuf_size--; } } break; case 'u': { unsigned int v = va_arg(va,unsigned int); char tab[32]; int x=0; do { tab[x++] = v%10; v/=10; } while (v); if (has_prec == 2) while (x= 0 && outbuf_size>1) { outbuf[wroffs++] = '0' + tab[x]; outbuf_size--; } } break; case 'x': case 'X': { const char base = (c - 'x') + 'a'; unsigned int v = va_arg(va,unsigned int); char tab[32]; int x=0; do { tab[x++] = v&0xf; v>>=4; } while (v); if (has_prec == 2) while (x= 0 && outbuf_size>1) { if (tab[x] < 10) outbuf[wroffs++] = '0' + tab[x]; else outbuf[wroffs++] = base + tab[x] - 10; outbuf_size--; } } break; case 'f': { double v = va_arg(va,double); if (outbuf_size<64) { char tmp[64]; projectcontext_fastDoubleToString(v,tmp,has_prec?prec:6); const char *str = tmp; while (outbuf_size > 1 && *str) { outbuf[wroffs++] = *str++; outbuf_size--; } } else { const char *p=projectcontext_fastDoubleToString(v,outbuf+wroffs,has_prec?prec:6); int amt = (int) (p-(outbuf+wroffs)); wroffs += amt; outbuf_size-=amt; } } break; default: want_abort=true; break; } if (want_abort) { fmt=ofmt; break; } } outbuf += wroffs; outbuf[0] = 0; if (outbuf_size<2||!*fmt) return wroffs; #if defined(_WIN32) && defined(_MSC_VER) // _vsnprintf() does not always null terminate (see below) _vsnprintf(outbuf,outbuf_size,fmt,va); #else // vsnprintf() on non-win32, always null terminates vsnprintf(outbuf,outbuf_size,fmt,va); #endif int l; outbuf_size--; for (l = 0; l < outbuf_size && outbuf[l]; l ++) if (outbuf[l] == '\n') outbuf[l] = ' '; #if defined(_WIN32) && defined(_MSC_VER) // nul terminate for _vsnprintf() outbuf[l]=0; #endif return wroffs+l; } class ProjectStateContext_Mem : public ProjectStateContext { public: ProjectStateContext_Mem(WDL_HeapBuf *hb, int rwflags) { m_rwflags=rwflags; m_heapbuf=hb; m_pos=0; m_tmpflag=0; #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB memset(&m_compstream,0,sizeof(m_compstream)); m_usecomp=0; #endif } virtual ~ProjectStateContext_Mem() { #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB if (m_usecomp==1) { FlushComp(true); deflateEnd(&m_compstream); } else if (m_usecomp==2) { inflateEnd(&m_compstream); } #endif }; virtual void WDL_VARARG_WARN(printf,2,3) AddLine(const char *fmt, ...); virtual int GetLine(char *buf, int buflen); // returns -1 on eof virtual WDL_INT64 GetOutputSize() { return m_heapbuf ? m_heapbuf->GetSize() : 0; } virtual int GetTempFlag() { return m_tmpflag; } virtual void SetTempFlag(int flag) { m_tmpflag=flag; } int m_pos; WDL_HeapBuf *m_heapbuf; int m_tmpflag; int m_rwflags; #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB int DecompressData() { if (m_pos >= m_heapbuf->GetSize()) return 1; m_compstream.next_in = (unsigned char *)m_heapbuf->Get() + m_pos; m_compstream.avail_in = m_heapbuf->GetSize()-m_pos; m_compstream.total_in = 0; int outchunk = 65536; m_compstream.next_out = (unsigned char *)m_compdatabuf.Add(NULL,outchunk); m_compstream.avail_out = outchunk; m_compstream.total_out = 0; int e = inflate(&m_compstream,Z_NO_FLUSH); m_pos += m_compstream.total_in; m_compdatabuf.Add(NULL,m_compstream.total_out - outchunk); // rewind return e != Z_OK; } void FlushComp(bool eof) { while (m_compdatabuf.Available()>=WDL_MEMPROJECTCONTEXT_ZLIB_CHUNKSIZE || eof) { if (!m_heapbuf->GetSize()) m_heapbuf->SetGranul(256*1024); m_compstream.next_in = (unsigned char *)m_compdatabuf.Get(); m_compstream.avail_in = m_compdatabuf.Available(); m_compstream.total_in = 0; int osz = m_heapbuf->GetSize(); int newsz=osz + wdl_max(m_compstream.avail_in,8192) + 256; m_compstream.next_out = (unsigned char *)m_heapbuf->Resize(newsz, false) + osz; if (m_heapbuf->GetSize()!=newsz) return; // ERROR m_compstream.avail_out = newsz-osz; m_compstream.total_out=0; deflate(&m_compstream,eof?Z_SYNC_FLUSH:Z_NO_FLUSH); m_heapbuf->Resize(osz+m_compstream.total_out,false); m_compdatabuf.Advance(m_compstream.total_in); if (m_compstream.avail_out) break; // no need to process anymore } m_compdatabuf.Compact(); } // these will be used for either decompression or compression, fear int m_usecomp; // 0=?, -1 = fail, 1=yes WDL_Queue m_compdatabuf; z_stream m_compstream; #endif }; // returns length, modifies ptr to point to tmp if newline needed to be filtered static int filter_newline_buf(const char **ptr, char *tmp, int tmpsz) { const char *use_buf = *ptr; if (!use_buf) return -1; int l; for (l=0; use_buf[l] && use_buf[l] != '\n'; l++); if (!use_buf[l]) return l; lstrcpyn_safe(tmp,use_buf,tmpsz); *ptr=tmp; if (l >= tmpsz) return tmpsz-1; for (;tmp[l]; l++) if (tmp[l] == '\n') tmp[l] = ' '; // replace any newlines with spaces return l; } void ProjectStateContext_Mem::AddLine(const char *fmt, ...) { if (!m_heapbuf || !(m_rwflags&2)) return; char tmp[8192]; const char *use_buf; int l; va_list va; va_start(va,fmt); if (fmt && fmt[0] == '%' && (fmt[1] == 's' || fmt[1] == 'S') && !fmt[2]) { use_buf = va_arg(va,const char *); l = filter_newline_buf(&use_buf,tmp,(int)sizeof(tmp)) + 1; } else { l = ProjectContextFormatString(tmp,sizeof(tmp),fmt, va) + 1; use_buf = tmp; } va_end(va); if (l < 1) return; #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB if (!m_usecomp) { if (deflateInit(&m_compstream,WDL_MEMPROJECTCONTEXT_USE_ZLIB)==Z_OK) m_usecomp=1; else m_usecomp=-1; } if (m_usecomp==1) { m_compdatabuf.Add(use_buf,(int)l); FlushComp(false); return; } #endif const int sz=m_heapbuf->GetSize(); if (!sz && m_heapbuf->GetGranul() < 256*1024) { m_heapbuf->SetGranul(256*1024); } char *p = (char *)m_heapbuf->ResizeOK(sz+l); if (!p) { // ERROR, resize to 0 and return m_heapbuf->Resize(0); m_heapbuf=0; return; } memcpy(p+sz,use_buf,l); } int ProjectStateContext_Mem::GetLine(char *buf, int buflen) // returns -1 on eof { if (!m_heapbuf || !(m_rwflags&1)) return -1; buf[0]=0; #ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB if (!m_usecomp) { unsigned char hdr[]={0x78,0x01}; if (m_heapbuf->GetSize()>2 && !memcmp(hdr,m_heapbuf->Get(),4) && inflateInit(&m_compstream)==Z_OK) m_usecomp=2; else m_usecomp=-1; } if (m_usecomp==2) { int x=0; for (;;) { const char *p = (const char*) m_compdatabuf.Get(); for (x = 0; x < m_compdatabuf.Available() && p[x] && p[x] != '\r' && p[x] != '\n'; x ++); while (x >= m_compdatabuf.Available()) { int err = DecompressData(); p = (const char *)m_compdatabuf.Get(); for (; x < m_compdatabuf.Available() && p[x] && p[x] != '\r' && p[x] != '\n'; x ++); if (err) break; } if (x||!m_compdatabuf.Available()) break; m_compdatabuf.Advance(1); // skip over nul or newline char } if (!x) return -1; if (buflen > 0 && buf) { int l = wdl_min(buflen-1,x); memcpy(buf,m_compdatabuf.Get(),l); buf[l]=0; } m_compdatabuf.Advance(x+1); m_compdatabuf.Compact(); return 0; } #endif int avail = m_heapbuf->GetSize() - m_pos; const char *p=(const char *)m_heapbuf->Get() + m_pos; while (avail > 0 && (p[0] =='\r'||p[0]=='\n'||!p[0]||p[0] == ' ' || p[0] == '\t')) { p++; m_pos++; avail--; } if (avail <= 0) return -1; int x; for (x = 0; x < avail && p[x] && p[x] != '\n'; x ++); m_pos += x+1; if (buflen > 0&&buf) { int l = buflen-1; if (l>x) l=x; memcpy(buf,p,l); if (l>0 && buf[l-1]=='\r') l--; buf[l]=0; } return 0; } class ProjectStateContext_File : public ProjectStateContext { public: ProjectStateContext_File(WDL_FileRead *rd, WDL_FileWrite *wr) { m_rd=rd; m_wr=wr; m_indent=0; m_bytesOut=0; m_errcnt=false; m_tmpflag=0; _rdbuf_pos = _rdbuf_valid = 0; } virtual ~ProjectStateContext_File(){ delete m_rd; delete m_wr; }; virtual void WDL_VARARG_WARN(printf,2,3) AddLine(const char *fmt, ...); virtual int GetLine(char *buf, int buflen); // returns -1 on eof virtual WDL_INT64 GetOutputSize() { return m_bytesOut; } virtual int GetTempFlag() { return m_tmpflag; } virtual void SetTempFlag(int flag) { m_tmpflag=flag; } bool HasError() { return m_errcnt; } WDL_INT64 m_bytesOut WDL_FIXALIGN; WDL_FileRead *m_rd; WDL_FileWrite *m_wr; char rdbuf[4096]; int _rdbuf_pos, _rdbuf_valid; int m_indent; int m_tmpflag; bool m_errcnt; }; int ProjectStateContext_File::GetLine(char *buf, int buflen) { if (!m_rd||buflen<3) return -1; char * const buf_orig=buf; int rdpos = _rdbuf_pos; int rdvalid = _rdbuf_valid; buflen -= 2; for (;;) { while (rdpos < rdvalid) { char c=rdbuf[rdpos++]; switch (c) { case ' ': case '\r': case '\n': case '\t': break; default: *buf++=c; do { int mxl = rdvalid - rdpos; if (mxl > buflen) mxl=buflen; while (mxl-->0) { char c2 = rdbuf[rdpos++]; if (c2=='\n') goto finished; *buf++ = c2; buflen--; } if (rdpos>=rdvalid) { rdpos = 0; rdvalid = m_rd->Read(rdbuf, sizeof(rdbuf)); if (rdvalid<1) break; } } while (buflen > 0); finished: _rdbuf_pos=rdpos; _rdbuf_valid=rdvalid; if (buf > buf_orig && buf[-1] == '\r') buf--; *buf=0; return 0; } } rdpos = 0; rdvalid = m_rd->Read(rdbuf, sizeof(rdbuf)); if (rdvalid<1) { buf[0]=0; return -1; } } } void ProjectStateContext_File::AddLine(const char *fmt, ...) { if (m_wr && !m_errcnt) { int err=0; char tmp[8192]; const char *use_buf; va_list va; va_start(va,fmt); int l; if (fmt && fmt[0] == '%' && (fmt[1] == 's' || fmt[1] == 'S') && !fmt[2]) { // special case "%s" passed, directly use it use_buf = va_arg(va,const char *); l = filter_newline_buf(&use_buf,tmp,(int)sizeof(tmp)); } else { l = ProjectContextFormatString(tmp,sizeof(tmp),fmt, va); use_buf = tmp; } va_end(va); if (l < 0) return; int a=m_indent; if (use_buf[0] == '<') m_indent+=2; else if (use_buf[0] == '>') a=(m_indent-=2); if (a>0) { m_bytesOut+=a; char tb[128]; memset(tb,' ',a < (int)sizeof(tb) ? a : (int)sizeof(tb)); while (a>0) { const int tl = a < (int)sizeof(tb) ? a : (int)sizeof(tb); a-=tl; m_wr->Write(tb,tl); } } err |= m_wr->Write(use_buf,l) != l; err |= m_wr->Write("\r\n",2) != 2; m_bytesOut += 2 + l; if (err) m_errcnt=true; } } ProjectStateContext *ProjectCreateFileRead(const char *fn) { WDL_FileRead *rd = new WDL_FileRead(fn,0,65536,1); if (!rd || !rd->IsOpen()) { delete rd; return NULL; } return new ProjectStateContext_File(rd,NULL); } ProjectStateContext *ProjectCreateFileWrite(const char *fn) { WDL_FileWrite *wr = new WDL_FileWrite(fn); if (!wr || !wr->IsOpen()) { delete wr; return NULL; } return new ProjectStateContext_File(NULL,wr); } ProjectStateContext *ProjectCreateMemCtx(WDL_HeapBuf *hb) { return new ProjectStateContext_Mem(hb,3); } ProjectStateContext *ProjectCreateMemCtx_Read(const WDL_HeapBuf *hb) { return new ProjectStateContext_Mem((WDL_HeapBuf *)hb,1); } ProjectStateContext *ProjectCreateMemCtx_Write(WDL_HeapBuf *hb) { return new ProjectStateContext_Mem(hb,2); } class ProjectStateContext_FastQueue : public ProjectStateContext { public: ProjectStateContext_FastQueue(WDL_FastQueue *fq) { m_fq = fq; m_tmpflag=0; } virtual ~ProjectStateContext_FastQueue() { }; virtual void WDL_VARARG_WARN(printf,2,3) AddLine(const char *fmt, ...); virtual int GetLine(char *buf, int buflen) { return -1; }//unsup virtual WDL_INT64 GetOutputSize() { return m_fq ? m_fq->Available() : 0; } virtual int GetTempFlag() { return m_tmpflag; } virtual void SetTempFlag(int flag) { m_tmpflag=flag; } WDL_FastQueue *m_fq; int m_tmpflag; }; void ProjectStateContext_FastQueue::AddLine(const char *fmt, ...) { if (!m_fq) return; va_list va; va_start(va,fmt); char tmp[8192]; if (fmt && fmt[0] == '%' && (fmt[1] == 's' || fmt[1] == 'S') && !fmt[2]) { const char *use_buf = va_arg(va,const char *); const int l = filter_newline_buf(&use_buf,tmp,(int)sizeof(tmp)); if (use_buf) m_fq->Add(use_buf, l + 1); } else { const int l = ProjectContextFormatString(tmp,sizeof(tmp),fmt, va); if (l>0) m_fq->Add(tmp, l+1); } va_end(va); } ProjectStateContext *ProjectCreateMemWriteFastQueue(WDL_FastQueue *fq) // only write! { return new ProjectStateContext_FastQueue(fq); } bool ProjectContext_GetNextLine(ProjectStateContext *ctx, LineParser *lpOut) { for (;;) { char linebuf[4096]; if (ctx->GetLine(linebuf,sizeof(linebuf))) { lpOut->parse(""); return false; } if (lpOut->parse(linebuf)||lpOut->getnumtokens()<=0) continue; return true; // success! } } bool ProjectContext_EatCurrentBlock(ProjectStateContext *ctx, ProjectStateContext *ctxOut) { int child_count=1; if (ctx) for (;;) { char linebuf[4096]; if (ctx->GetLine(linebuf,sizeof(linebuf))) break; const char *sp = linebuf; while (*sp == ' ' || *sp == '\t') sp++; const char *p = sp; if (*p == '\'' || *p == '"' || *p == '`') p++; // skip a quote if any if (p[0] == '>') if (--child_count < 1) return true; if (ctxOut) ctxOut->AddLine("%s",sp); if (p[0] == '<') child_count++; } return false; } #include "wdl_base64.h" int cfg_decode_binary(ProjectStateContext *ctx, WDL_HeapBuf *hb) // 0 on success, doesnt clear hb { int child_count=1; for (;;) { char linebuf[4096]; if (ctx->GetLine(linebuf,sizeof(linebuf))) break; const char *p = linebuf; while (*p == ' ' || *p == '\t') p++; if (*p == '\'' || *p == '"' || *p == '`') p++; // skip a quote if any if (p[0] == '<') child_count++; else if (p[0] == '>') { if (child_count-- == 1) return 0; } else if (child_count == 1 && p[0]) { unsigned char buf[3200]; const int buf_l=wdl_base64decode(p,buf,sizeof(buf)); if (buf_l) { const int os=hb->GetSize(); char *dest = (char*)hb->ResizeOK(os+buf_l); if (dest) memcpy(dest+os,buf,buf_l); } } } return -1; } void cfg_encode_binary(ProjectStateContext *ctx, const void *ptr, int len) { if (!ctx || len < 1) return; const unsigned char *p=(const unsigned char *)ptr; if (len > 128 && len < (1<<30)) { // we could (probably should) use dynamic_cast<> here, but as we span modules this // raises all kinds of questions (especially with VC having the option to disable RTTI). // for now, we assume that the first void * in an object is the vtable pointer. with // testing, of course. WDL_FastQueue *fq = NULL; WDL_HeapBuf *hb = NULL; #ifndef WDL_MEMPROJECTCONTEXT_USE_ZLIB static const ProjectStateContext_Mem hb_def(NULL,0); #endif static const ProjectStateContext_FastQueue fq_def(NULL); if (*(void **)ctx == *(void **)&fq_def) { fq=((ProjectStateContext_FastQueue*)ctx)->m_fq; } #ifndef WDL_MEMPROJECTCONTEXT_USE_ZLIB else if (*(void **)ctx == *(void **)&hb_def) { hb=((ProjectStateContext_Mem*)ctx)->m_heapbuf; } #endif if (fq||hb) { const int linelen8 = 280/8; const int enc_len = ((len+2)/3)*4; // every 3 characters end up as 4 const int lines = (enc_len + linelen8*8 - 1) / (linelen8*8); char *wr = NULL; if (fq) { wr = (char*)fq->Add(WDL_FASTQUEUE_ADD_NOZEROBUF,enc_len + lines); } else if (hb) { const int oldsz=hb->GetSize(); wr=(char*)hb->ResizeOK(oldsz + enc_len + lines,false); if (wr) wr+=oldsz; } if (wr) { #ifdef _DEBUG char * const wr_end=wr + enc_len + lines; #endif int lpos = 0; while (len >= 6) { const int accum = (p[0] << 16) + (p[1] << 8) + p[2]; const int accum2 = (p[3] << 16) + (p[4] << 8) + p[5]; wr[0] = wdl_base64_alphabet[(accum >> 18) & 0x3F]; wr[1] = wdl_base64_alphabet[(accum >> 12) & 0x3F]; wr[2] = wdl_base64_alphabet[(accum >> 6) & 0x3F]; wr[3] = wdl_base64_alphabet[accum & 0x3F]; wr[4] = wdl_base64_alphabet[(accum2 >> 18) & 0x3F]; wr[5] = wdl_base64_alphabet[(accum2 >> 12) & 0x3F]; wr[6] = wdl_base64_alphabet[(accum2 >> 6) & 0x3F]; wr[7] = wdl_base64_alphabet[accum2 & 0x3F]; wr+=8; p+=6; len-=6; if (++lpos >= linelen8) { *wr++= 0; lpos=0; } } if (len >= 3) { const int accum = (p[0] << 16) + (p[1] << 8) + p[2]; wr[0] = wdl_base64_alphabet[(accum >> 18) & 0x3F]; wr[1] = wdl_base64_alphabet[(accum >> 12) & 0x3F]; wr[2] = wdl_base64_alphabet[(accum >> 6) & 0x3F]; wr[3] = wdl_base64_alphabet[accum & 0x3F]; wr+=4; p+=3; len-=3; lpos+=3; } if (len>0) { lpos += len; if (len == 2) { const int accum = (p[0] << 8) | p[1]; wr[0] = wdl_base64_alphabet[(accum >> 10) & 0x3F]; wr[1] = wdl_base64_alphabet[(accum >> 4) & 0x3F]; wr[2] = wdl_base64_alphabet[(accum & 0xF)<<2]; } else { const int accum = p[0]; wr[0] = wdl_base64_alphabet[(accum >> 2) & 0x3F]; wr[1] = wdl_base64_alphabet[(accum & 0x3)<<4]; wr[2] = '='; } wr[3] = '='; wr+=4; } if (lpos>0) *wr++=0; #ifdef _DEBUG if (wr != wr_end) wdl_log("cfg_encode_binary: block mode size mismatch %d!\n", (int)(wr-wr_end)); #endif return; } } } do { char buf[256]; int thiss=len; if (thiss > 96) thiss=96; wdl_base64encode(p,buf,thiss); ctx->AddLine("%s",buf); p+=thiss; len-=thiss; } while (len>0); } int cfg_decode_textblock(ProjectStateContext *ctx, WDL_FastString *str) // 0 on success, appends to str { int child_count=1; bool did_firstline=!!str->Get()[0]; for (;;) { char linebuf[4096]; if (ctx->GetLine(linebuf,sizeof(linebuf))) break; const char *p = linebuf; while (*p == ' ' || *p == '\t') p++; if (*p == '\'' || *p == '"' || *p == '`') p++; // skip a quote if any if (!p[0]) continue; else if (p[0] == '<') child_count++; else if (p[0] == '>') { if (child_count-- == 1) return 0; } else if (child_count == 1) { const char *prefix = did_firstline ? "\r\n" : ""; // lines can have a prefix immediately before | to specify the line ending of the previous line switch (p[0]) { case 'c': prefix=""; p++; break; case 'n': prefix="\n"; p++; break; case 'r': prefix="\r"; p++; break; case 'R': prefix="\n\r"; p++; break; } if (p[0] == '|') { if (*prefix) str->Append(prefix); str->Append(++p); did_firstline=true; } } } return -1; } void cfg_encode_textblock(ProjectStateContext *ctx, const char *txt) // splits long lines { while (*txt) { int l = 0; while (txt[l] && l < 4000 && txt[l] != '\r' && txt[l] != '\n') l++; ctx->AddLine("|%.*s",l,txt); txt += l; if (*txt == '\r') { if (*++txt== '\n') txt++; } else if (*txt == '\n') { if (*++txt == '\r') txt++; } } } void cfg_encode_textblock2(ProjectStateContext *ctx, const char *txt) // preserves long lines, line endings { char prefix = ' '; while (*txt) { int l = 0; while (txt[l] && l < 2000 && txt[l] != '\r' && txt[l] != '\n') l++; ctx->AddLine("%c|%.*s",prefix,l,txt); txt += l; if (*txt == '\r') { prefix = 'r'; if (*++txt== '\n') { txt++; prefix = ' '; } } else if (*txt == '\n') { prefix = 'n'; if (*++txt == '\r') { txt++; prefix = 'R'; } } else if (*txt) prefix = 'c'; else break; if (!*txt) ctx->AddLine("%c|",prefix); } } char getConfigStringQuoteChar(const char *p, bool prefer_quoteless) { if (!p || !*p) return '"'; char fc = *p; int flags=0; while (*p && flags!=15) { char c=*p++; if (c=='"') flags|=1; else if (c=='\'') flags|=2; else if (c=='`') flags|=4; else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') flags |= 8; } if (prefer_quoteless || flags == 7) { if (!(flags & 8) && fc != '"' && fc != '\'' && fc != '`' && fc != '#' && fc != ';') return ' '; } if (!(flags & 1)) return '"'; if (!(flags & 2)) return '\''; if (!(flags & 4)) return '`'; return 0; } bool configStringWantsBlockEncoding(const char *in) // returns true if over 1k long, has newlines, or contains all quote chars { int maxl = 1024, flags = 0; while (--maxl) { switch (*in++) { case 0: return false; case '\n': return true; case '"': if ((flags|=1)==7) return true; break; case '`': if ((flags|=2)==7) return true; break; case '\'': if ((flags|=4)==7) return true; break; } } return true; } void makeEscapedConfigString(const char *in, WDL_FastString *out) { char c; if (!in || !*in) out->Set("\"\""); else if ((c = getConfigStringQuoteChar(in))) { if (c == ' ') { out->Set(in); } else { out->Set(&c,1); out->Append(in); out->Append(&c,1); } } else // ick, change ` into ' { out->Set("`"); out->Append(in); out->Append("`"); char *p=(char *)out->Get()+1; while (*p && p[1]) { if (*p == '`') *p='\''; else if (*p == '\r' || *p == '\n') *p=' '; p++; } } }