U0 SPutChar(U8 **_dst,U8 ch,U8 **_buf) { I64 i; U8 *dst=*_dst,*buf; if (_buf) { buf=*_buf; i=dst-buf; if (i>=MSize(buf)) { buf=MAlloc(i<<1+1); MemCpy(buf,*_buf,i); Free(*_buf); dst=buf+i; *_buf=buf; } } *dst++=ch; *_dst=dst; } U0 SPutChars(U8 **_dst,U8 *src,U8 **_buf, I64 len) { I64 i; if (len<=0) return; U8 *dst=*_dst,*buf; if (_buf) { buf=*_buf; i=dst-buf; if (i+len>=MSize(buf)) { buf=MAlloc((i+len)<<1+1); MemCpy(buf,*_buf,i); Free(*_buf); dst=buf+i; *_buf=buf; } } MemCpy(dst,src,len); *_dst=dst+len; } U0 SetChars(U8 **_dst,I64 ch,U8 **_buf, I64 len) { I64 i; if (len<=0) return; U8 *dst=*_dst,*buf; if (_buf) { buf=*_buf; i=dst-buf; if (i+len>=MSize(buf)) { buf=MAlloc((i+len)<<1+1); MemCpy(buf,*_buf,i); Free(*_buf); dst=buf+i; *_buf=buf; } } MemSet(dst,ch,len); *_dst=dst+len; } U0 OutStr(U8 *ptr,U8 **_buf,U8 **_dst,I64 len,I64 flags) { I64 i; if (!ptr) i=0; else i=StrLen(ptr); if (flags&PRTF_TRUNCATE && i>len) i=len; if (flags&PRTF_LEFT_JUSTIFY) { SPutChars(_dst,ptr,_buf,i); SetChars(_dst,CH_SPACE,_buf,len-i); } else { SetChars(_dst,CH_SPACE,_buf,len-i); SPutChars(_dst,ptr,_buf,i); } } U8 *MPrintTime(CDate cdt) { CDateStruct ds; Date2Struct(&ds,cdt+local_time_offset); return MStrPrint("%02d:%02d:%02d",ds.hour,ds.min,ds.sec); } U8 *MPrintDate(CDate cdt) { CDateStruct ds; Date2Struct(&ds,cdt+local_time_offset); return MStrPrint("%02d/%02d/%02d",ds.mon,ds.day_of_mon,ds.year%100); } U8 *MPrintQ(U8 *ptr,I64 flags) { U8 **_buf,*buf,**_dst,*dst,buf2[8],*ptr2; I64 ch; buf=MAlloc(STR_LEN); _buf=&buf; dst=buf; _dst=&dst; if (ptr) while (ch=*ptr++) { switch (ch) { case '$': if (flags&PRTF_DOLLAR) { SPutChar(_dst,'\\',_buf); SPutChar(_dst,'d',_buf); } else { SPutChar(_dst,ch,_buf); SPutChar(_dst,ch,_buf); } break; case '%': SPutChar(_dst,ch,_buf); if (flags&PRTF_SLASH) SPutChar(_dst,ch,_buf); break; case '\n': SPutChar(_dst,'\\',_buf); SPutChar(_dst,'n',_buf); break; case '\r': SPutChar(_dst,'\\',_buf); SPutChar(_dst,'r',_buf); break; case '\t': SPutChar(_dst,'\\',_buf); SPutChar(_dst,'t',_buf); break; case '"': case '\\': SPutChar(_dst,'\\',_buf); SPutChar(_dst,ch,_buf); break; default: if (ch>=CH_SHIFT_SPACE && ch!=0x7F) SPutChar(_dst,ch,_buf); else { StrPrint(buf2,"\\x%02X",ch); ptr2=buf2; while (*ptr2) SPutChar(_dst,*ptr2++,_buf); } } } SPutChar(_dst,0,_buf); return buf; } U8 *MPrintq(U8 *ptr,I64 flags) { U8 **_buf,*buf,**_dst,*dst; I64 i,j,ch,ch1; buf=MAlloc(STR_LEN); _buf=&buf; dst=buf; _dst=&dst; if (ptr) while (ch=*ptr++) { ch1=*ptr; switch (ch) { case '\\': switch (ch1) { start: case '0': SPutChar(_dst,0,_buf); break; case '\'': SPutChar(_dst,'\'',_buf); break; case '\`': SPutChar(_dst,'\`',_buf); break; case '"': SPutChar(_dst,'"',_buf); break; case '\\': SPutChar(_dst,'\\',_buf); break; case 'd': SPutChar(_dst,'$',_buf); break; case 'n': SPutChar(_dst,'\n',_buf); break; case 'r': SPutChar(_dst,'\r',_buf); break; case 't': SPutChar(_dst,'\t',_buf); break; end: ptr++; break; case 'x': case 'X': i=0; ptr++; for (j=0; j<2; j++) { ch1=ToUpper(*ptr++); if (Bt(char_bmp_hex_numeric,ch1)) { if (ch1<='9') i=i<<4+ch1-'0'; else i=i<<4+ch1-'A'+10; } else { ptr--; break; } } SPutChar(_dst,i,_buf); break; default: SPutChar(_dst,ch,_buf); } break; case '$': SPutChar(_dst,ch,_buf); if (ch1=='$') ptr++; break; case '%': SPutChar(_dst,ch,_buf); if (flags&PRTF_SLASH && ch1=='%') ptr++; break; default: SPutChar(_dst,ch,_buf); } } SPutChar(_dst,0,_buf); return buf; } U8 *sys_pos_pows_lets=" KMGTPEZY", *sys_neg_pows_lets=" munpfazy", *sys_pos_pows_lst="kilo\0mega\0giga\0tera\0peta\0exa\0zetta\0yotta\0", *sys_neg_pows_lst="milli\0micro\0nano\0pico\0femto\0atto\0zepto\0yocto\0"; #define TMP_BUF_LEN 256 #define SLOP 8 U8 *StrPrintJoin(U8 *dst,U8 *fmt,I64 argc,I64 *argv) { /*Print("") Fmt Strings In float formatting, do not exceed 18-digits before or after the decimal point because the numbers before and after the decimal point are stored in 64-bits. Use exponentiated forms to avoid this. */ I64 i,j,l,ch,k,k0,n,n0,len,dec_len,flags,old_flags, aux_fmt_num,comma_cnt,comma_fmt_cnt,cur_arg=0; U64 m; F64 d,d1; CDoc *doc; U8 *ptr,**_buf,*buf,**_dst,tmp_buf[TMP_BUF_LEN],tmp_buf2[TMP_BUF_LEN*2]; if (!fmt) throw('StrPrint'); if (dst) { _buf=NULL; buf=dst; } else { buf=MAlloc(STR_LEN); _buf=&buf; dst=buf; } _dst=&dst; while (ch = *fmt++) { if (ch=='%') { flags=0; if (*fmt=='-') { flags|=PRTF_LEFT_JUSTIFY; fmt++; } if (*fmt=='0') { flags|=PRTF_PAD_ZERO; fmt++; } len=0; while ('0'<=*fmt<='9') len=len*10+ *fmt++ -'0'; if (*fmt=='*') { fmt++; if (cur_arg>=argc) throw('StrPrint'); len=argv[cur_arg++]; } dec_len=0; if (*fmt=='.') { fmt++; while ('0'<=*fmt<='9') dec_len=dec_len*10+ *fmt++ -'0'; if (*fmt=='*') { fmt++; if (cur_arg>=argc) throw('StrPrint'); dec_len=argv[cur_arg++]; } flags|=PRTF_DECIMAL; } aux_fmt_num=0; while (TRUE) { switch (*fmt) { start: case '$': flags|=PRTF_DOLLAR; break; case '/': flags|=PRTF_SLASH; break; case ',': flags|=PRTF_COMMA; break; case 't': flags|=PRTF_TRUNCATE; break; case 'l': //harmless break; end: fmt++; break; case 'h': fmt++; flags|=PRTF_AUX_FMT_NUM; if (*fmt=='?') { fmt++; flags|=PRTF_QUESTION; } else { if (*fmt=='*') { fmt++; if (cur_arg>=argc) throw('StrPrint'); aux_fmt_num=argv[cur_arg++]; } else { if (*fmt=='-') { fmt++; flags|=PRTF_NEG_AUX_FMT_NUM; } while ('0'<=*fmt<='9') aux_fmt_num=aux_fmt_num*10+ *fmt++ -'0'; if (flags&PRTF_NEG_AUX_FMT_NUM) aux_fmt_num=-aux_fmt_num; } } break; default: goto sp_arg; } } sp_arg: k=0; switch (*fmt++) { start: case 'F': if (cur_arg>=argc) throw('StrPrint'); if (flags&PRTF_DOLLAR) { doc=argv[cur_arg++]; old_flags=doc->flags; doc->flags|=DOCF_NO_CURSOR; ptr=DocSave(doc); doc->flags=old_flags; } else ptr=FileRead(argv[cur_arg++]); break; case 'Q': if (cur_arg>=argc) throw('StrPrint'); ptr=MPrintQ(argv[cur_arg++],flags); break; case 'q': if (cur_arg>=argc) throw('StrPrint'); ptr=MPrintq(argv[cur_arg++],flags); break; case 'D': if (cur_arg>=argc) throw('StrPrint'); ptr=MPrintDate(argv[cur_arg++]); break; case 'T': if (cur_arg>=argc) throw('StrPrint'); ptr=MPrintTime(argv[cur_arg++]); break; end: OutStr(ptr,_buf,_dst,len,flags); Free(ptr); break; start: case 's': if (cur_arg>=argc) throw('StrPrint'); ptr=argv[cur_arg++]; break; case 'S': if (cur_arg>=argc) throw('StrPrint'); ptr=Define(argv[cur_arg++]); break; case 'z': if (cur_arg+1>=argc) throw('StrPrint'); ptr=LstSub(argv[cur_arg],argv[cur_arg+1]); cur_arg=cur_arg+2; break; case 'Z': if (cur_arg+1>=argc) throw('StrPrint'); ptr=DefineSub(argv[cur_arg],argv[cur_arg+1]); cur_arg=cur_arg+2; break; end: OutStr(ptr,_buf,_dst,len,flags); break; start: case 'c': if (cur_arg>=argc) throw('StrPrint'); tmp_buf[0](I64)=argv[cur_arg++]; tmp_buf[8]=0; break; case 'C': if (cur_arg>=argc) throw('StrPrint'); tmp_buf[0](I64)=argv[cur_arg++]; tmp_buf[8]=0; ptr=tmp_buf; while (*ptr) { *ptr=ToUpper(*ptr); ptr++; } break; end: if (!(flags&PRTF_AUX_FMT_NUM)) aux_fmt_num=1; while (aux_fmt_num-->0) OutStr(tmp_buf,_buf,_dst,len,flags); break; case 'p': if (cur_arg>=argc) throw('StrPrint'); StrPrintFunSeg(tmp_buf,argv[cur_arg++],len,flags); OutStr(tmp_buf,_buf,_dst,len,flags); break; case 'P': if (cur_arg>=argc) throw('StrPrint'); StrPrintFunSeg(tmp_buf,argv[cur_arg],len,flags); if (!IsRaw || !_buf) { StrPrint(tmp_buf2,"$LK,\"%s\",A=\"AD:0x%X\"$", tmp_buf,argv[cur_arg]); OutStr(tmp_buf2,_buf,_dst,len,flags); } else OutStr(tmp_buf,_buf,_dst,len,flags); cur_arg++; break; case 'd': if (cur_arg>=argc) throw('StrPrint'); m=argv[cur_arg++]; if (m(I64)<0) { flags|=PRTF_NEG; m=-m; } sp_out_dec: if (flags&PRTF_AUX_FMT_NUM) { if (!len) len=12; d=m; goto sp_out_eng; } if (flags&PRTF_COMMA) { comma_fmt_cnt=comma_cnt=3; do { tmp_buf[k++]=ModU64(&m,10)+'0'; if (!m) break; if (!--comma_cnt) { tmp_buf[k++]=','; comma_cnt=3; } } while (k<TMP_BUF_LEN-SLOP); sp_out_comma_num: if (flags&PRTF_NEG) i=1; else i=0; if (len<0) len=0; if (flags&PRTF_TRUNCATE && k+i>len) k=len-i; if (k<0) k=0; if (flags&PRTF_PAD_ZERO) { if (flags&PRTF_NEG) SPutChar(_dst,'-',_buf); comma_cnt=(len-k-i+comma_fmt_cnt-comma_cnt+1) %(comma_fmt_cnt+1)+1; for (; i<len-k; i++) { if (!--comma_cnt) { SPutChar(_dst,',',_buf); comma_cnt=comma_fmt_cnt; if (++i>=len-k) break; } SPutChar(_dst,'0',_buf); } } else { for (; i<len-k; i++) SPutChar(_dst,CH_SPACE,_buf); if (flags&PRTF_NEG) SPutChar(_dst,'-',_buf); } } else { do { tmp_buf[k++]=ModU64(&m,10)+'0'; if (!m) break; } while (k<TMP_BUF_LEN-SLOP); sp_out_num: if (flags&PRTF_NEG) i=1; else i=0; if (len<0) len=0; if (flags&PRTF_TRUNCATE && k+i>len) k=len-i; if (k<0) k=0; if (flags&PRTF_PAD_ZERO) { if (flags&PRTF_NEG) SPutChar(_dst,'-',_buf); for (; i<len-k; i++) SPutChar(_dst,'0',_buf); } else { for (; i<len-k; i++) SPutChar(_dst,CH_SPACE,_buf); if (flags&PRTF_NEG) SPutChar(_dst,'-',_buf); } } for (i=k-1; i>=0; i--) SPutChar(_dst,tmp_buf[i],_buf); break; case 'u': if (cur_arg>=argc) throw('StrPrint'); m=argv[cur_arg++]; goto sp_out_dec; case 'f': if (cur_arg>=argc) throw('StrPrint'); d=argv[cur_arg++](F64); if (d<0) { flags|=PRTF_NEG; d=-d; } if (d==inf) { sp_out_inf: if (flags&PRTF_NEG) i=1; else i=0; k=1; if (len<0) len=0; if (flags&PRTF_TRUNCATE && k+i>len) k=len-i; if (k<0) k=0; for (; i<len-k; i++) SPutChar(_dst,CH_SPACE,_buf); if (flags&PRTF_NEG) SPutChar(_dst,'-',_buf); for (i=0; i<k; i++) SPutChar(_dst,'inf',_buf); break; } sp_out_f: if (dec_len<0) dec_len=0; n=Log10(d); if (i=dec_len) { if (flags&PRTF_COMMA) i=i-i/4; if (n+i>17) { n+=i-17; d*=Pow10I64(i-n); } else { n=0; d*=Pow10I64(i); } i=dec_len; } else if (n>17) { n-=17; d*=Pow10I64(-n); } else n=0; m=Round(d); if (flags&PRTF_COMMA) { comma_cnt=i&3; while (i-- && k<TMP_BUF_LEN-SLOP) { if (i>2 && !comma_cnt--) { tmp_buf[k++]=','; comma_cnt=2; if (!--i) break; } if (n) { n--; tmp_buf[k++]='0'; } else tmp_buf[k++]=ModU64(&m,10)+'0'; if (!i) break; } } else { while (i-- && k<TMP_BUF_LEN-SLOP) { if (n) { n--; tmp_buf[k++]='0'; } else tmp_buf[k++]=ModU64(&m,10)+'0'; } } if (dec_len) tmp_buf[k++]='.'; if (flags&PRTF_COMMA) { comma_cnt=3; do { if (n) { n--; tmp_buf[k++]='0'; } else tmp_buf[k++]=ModU64(&m,10)+'0'; if (!m) break; if (!--comma_cnt) { tmp_buf[k++]=','; comma_cnt=3; } } while (k<TMP_BUF_LEN-SLOP); } else { do { if (n) { n--; tmp_buf[k++]='0'; } else tmp_buf[k++]=ModU64(&m,10)+'0'; if (!m) break; } while (k<TMP_BUF_LEN-SLOP); } goto sp_out_num; case 'e': if (!len) len=12; flags|=PRTF_TRUNCATE; if (cur_arg>=argc) throw('StrPrint'); d=argv[cur_arg++](F64); if (d<0) { flags|=PRTF_NEG; d=-d; } if (d==inf) goto sp_out_inf; if (d) n=Floor(Log10(d)); else n=0; sp_out_e: d/=Pow10I64(n); k0=k; for (l=0; l<2; l++) { n0=n; if (n<0) { n=-n; flags|=PRTF_NEG_E; } else flags&=~PRTF_NEG_E; i=3; do tmp_buf[k++]=ModU64(&n,10)+'0'; while (n && i--); if (flags&PRTF_NEG_E) tmp_buf[k++]='-'; tmp_buf[k++]='e'; dec_len=len-k-2; if (flags&PRTF_NEG) dec_len--; if (d) { d1=d+Pow10I64(-dec_len)/2; if (d1<1.0) { d*=10; n=n0-1; k=k0; } else if (d1>=10) { d/=10; n=n0+1; k=k0; } else break; } else break; } goto sp_out_f; case 'g': if (!len) len=12; flags|=PRTF_TRUNCATE; if (cur_arg>=argc) throw('StrPrint'); d=argv[cur_arg++](F64); if (d<0) { flags|=PRTF_NEG; d=-d; } if (d==inf) goto sp_out_inf; if (d) n=Floor(Log10(d)); else n=0; if (n>=len-1-dec_len || n<-(dec_len-1)) goto sp_out_e; else goto sp_out_f; case 'n': if (!len) len=12; flags|=PRTF_TRUNCATE; if (cur_arg>=argc) throw('StrPrint'); d=argv[cur_arg++](F64); if (d<0) { flags|=PRTF_NEG; d=-d; } sp_out_eng: //Engineering notation if (d==inf) goto sp_out_inf; if (d) n=FloorI64(Floor(Log10(d)),3); else n=0; d/=Pow10I64(n); if (n<0) { n=-n; flags|=PRTF_NEG_E; } if (flags&PRTF_AUX_FMT_NUM && -24<=n<=24) { if (flags&PRTF_QUESTION) { if (flags&PRTF_NEG_E) i=-n/3; else i=n/3; j=0; } else { if (flags&PRTF_NEG_E) j=-n-aux_fmt_num; else j=n-aux_fmt_num; d*=Pow10I64(j); i=aux_fmt_num/3; } if (i<0) tmp_buf[k++]=sys_neg_pows_lets[-i]; else if (i>0) tmp_buf[k++]=sys_pos_pows_lets[i]; else if (len!=0) tmp_buf[k++]=CH_SPACE; if (!(flags&PRTF_DECIMAL)) { dec_len=len-k-2; if (flags&PRTF_NEG) dec_len--; if (j>0) { if (flags&PRTF_COMMA) dec_len-=4*j/3; else dec_len-=j; } d1=d+Pow10I64(-dec_len+1)/2; if (d1>=10) { dec_len--; if (d1>=100) dec_len--; } } } else { i=3; do tmp_buf[k++]=ModU64(&n,10)+'0'; while (n && i--); if (flags&PRTF_NEG_E) tmp_buf[k++]='-'; tmp_buf[k++]='e'; if (!dec_len) { dec_len=len-k-2; if (flags&PRTF_NEG) dec_len--; d1=d+Pow10I64(-dec_len+1)/2; if (d1>=10) { dec_len--; if (d1>=100) dec_len--; } } } if (flags&PRTF_COMMA) { if (len && dec_len>0 && !(dec_len&3)) tmp_buf[k++]=','; dec_len-=dec_len/4; } goto sp_out_f; case 'X': if (cur_arg>=argc) throw('StrPrint'); m=argv[cur_arg++]; if (flags&PRTF_COMMA) { comma_fmt_cnt=comma_cnt=4; do { tmp_buf[k]= m&15 +'0'; if (tmp_buf[k]>'9') tmp_buf[k]+='A'-0x3A; k++; m>>=4; if (!m) break; if (!--comma_cnt) { tmp_buf[k++]=','; comma_cnt=4; } } while (k<TMP_BUF_LEN-SLOP); goto sp_out_comma_num; } else { do { tmp_buf[k]= m&15 +'0'; if (tmp_buf[k]>'9') tmp_buf[k]+='A'-0x3A; k++; m>>=4; } while (m && k<TMP_BUF_LEN-SLOP); goto sp_out_num; } case 'x': if (cur_arg>=argc) throw('StrPrint'); m=argv[cur_arg++]; if (flags&PRTF_COMMA) { comma_fmt_cnt=comma_cnt=4; do { tmp_buf[k]= m&15 +'0'; if (tmp_buf[k]>'9') tmp_buf[k]+='a'-0x3A; k++; m>>=4; if (!m) break; if (!--comma_cnt) { tmp_buf[k++]=','; comma_cnt=4; } } while (k<TMP_BUF_LEN-SLOP); goto sp_out_comma_num; } else { do { tmp_buf[k]= m&15 +'0'; if (tmp_buf[k]>'9') tmp_buf[k]+='a'-0x3A; k++; m>>=4; } while (m && k<TMP_BUF_LEN-SLOP); goto sp_out_num; } case 'b': case 'B': if (cur_arg>=argc) throw('StrPrint'); m=argv[cur_arg++]; if (flags&PRTF_COMMA) { comma_fmt_cnt=comma_cnt=4; do { tmp_buf[k++]= m&1 +'0'; m>>=1; if (!m) break; if (!--comma_cnt) { tmp_buf[k++]=','; comma_cnt=4; } } while (k<TMP_BUF_LEN-SLOP); goto sp_out_comma_num; } else { do { tmp_buf[k++]= m&1 +'0'; m>>=1; } while (m && k<TMP_BUF_LEN-SLOP); goto sp_out_num; } case '%': SPutChar(_dst,'%',_buf); break; } } else { SPutChar(_dst,ch,_buf); } } SPutChar(_dst,0,_buf); return buf; } U8 *StrPrint(U8 *dst,U8 *fmt,...) {//See StrPrintJoin(). return StrPrintJoin(dst,fmt,argc,argv); } U8 *CatPrint(U8 *_dst,U8 *fmt,...) {//StrCat(). See StrPrintJoin(). U8 *dst=_dst; while (*dst) dst++; StrPrintJoin(dst,fmt,argc,argv); return _dst; } U0 Print(U8 *fmt,...) {//Print("") Fmt Strings. See StrPrintJoin(). //Don't use this. See Print() shortcut. U8 *buf=StrPrintJoin(NULL,fmt,argc,argv); PutS(buf);//Don't use PutS(). See Print() shortcut. Free(buf); } U8 *MStrPrint(U8 *fmt,...) {//MAlloc StrPrint. See StrPrintJoin(). U8 *res,*buf=StrPrintJoin(NULL,fmt,argc,argv); res=StrNew(buf); Free(buf); return res; } U0 PrintErr(U8 *fmt,...) {//Print "Err:" and msg in blinking red. U8 *buf=StrPrintJoin(NULL,fmt,argc,argv); GetOutOfDollar; if (IsRaw) "%,p %,p %,p %,p ERROR: %s",Caller,Caller(2),Caller(3),Caller(4),buf; else "%,p %,p %,p %,p " ST_ERR_ST "%s",Caller,Caller(2),Caller(3),Caller(4),buf; Free(buf); } U0 PrintWarn(U8 *fmt,...) {//Print "Warn:" and msg in blinking red. U8 *buf=StrPrintJoin(NULL,fmt,argc,argv); GetOutOfDollar; if (IsRaw) "%,p %,p %,p %,p WARNING: %s",Caller,Caller(2),Caller(3),Caller(4),buf; else "%,p %,p %,p %,p " ST_WARN_ST "%s",Caller,Caller(2),Caller(3),Caller(4),buf; Free(buf); }