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); MemCopy(buf, *_buf, i); Free(*_buf); dst = buf + i; *_buf = buf; } } *dst++ = ch; *_dst = dst; } U0 OutStr(U8 *ptr, U8 **_buf, U8 **_dst, I64 len, I64 flags) { I64 i, j; if (!ptr) i = 0; else i = StrLen(ptr); if (flags & PRINTF_TRUNCATE && i > len) i = len; if (flags & PRINTF_LEFT_JUSTIFY) { for (j = 0; j < i; j++) SPutChar(_dst, *ptr++, _buf); for (j = 0; j < len - i; j++) SPutChar(_dst, CH_SPACE, _buf); } else { for (j = 0; j < len - i; j++) SPutChar(_dst, CH_SPACE, _buf); for (j = len - i; j < len; j++) SPutChar(_dst, *ptr++, _buf); } } 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 & PRINTF_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 & PRINTF_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_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 & PRINTF_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_list = "kilo\0mega\0giga\0tera\0peta\0exa\0zetta\0yotta\0", *sys_neg_pows_list = "milli\0micro\0nano\0pico\0femto\0atto\0zepto\0yocto\0"; #define TMP_BUF_LEN 256 #define SLOP 8 U8 *StrPrintJoin(U8 *dst, U8 *format, I64 argc, I64 *argv) {/*Print("") Format 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_format_num, comma_count, comma_format_count, 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 (!format) throw('StrPrint'); if (dst) { _buf = NULL; buf = dst; } else { buf = MAlloc(STR_LEN); _buf = &buf; dst = buf; } _dst = &dst; while (ch = *format++) { if (ch == '%') { flags = 0; if (*format == '-') { flags |= PRINTF_LEFT_JUSTIFY; format++; } if (*format == '0') { flags |= PRINTF_PAD_ZERO; format++; } len = 0; while ('0' <= *format <= '9') len = len * 10 + *format++ - '0'; if (*format == '*') { format++; if (cur_arg >= argc) throw('StrPrint'); len = argv[cur_arg++]; } dec_len = 0; if (*format == '.') { format++; while ('0' <= *format <= '9') dec_len = dec_len * 10 + *format++ - '0'; if (*format == '*') { format++; if (cur_arg >= argc) throw('StrPrint'); dec_len = argv[cur_arg++]; } flags |= PRINTF_DECIMAL; } aux_format_num = 0; while (TRUE) { switch (*format) { start: case '$': flags |= PRINTF_DOLLAR; break; case '/': flags |= PRINTF_SLASH; break; case ',': flags |= PRINTF_COMMA; break; case 't': flags |= PRINTF_TRUNCATE; break; case 'l': //harmless break; end: format++; break; case 'h': format++; flags |= PRINTF_AUX_FORMAT_NUM; if (*format == '?') { format++; flags |= PRINTF_QUESTION; } else { if (*format == '*') { format++; if (cur_arg >= argc) throw('StrPrint'); aux_format_num = argv[cur_arg++]; } else { if (*format == '-') { format++; flags |= PRINTF_NEG_AUX_FORMAT_NUM; } while ('0' <= *format <= '9') aux_format_num = aux_format_num * 10 + *format++ - '0'; if (flags & PRINTF_NEG_AUX_FORMAT_NUM) aux_format_num = -aux_format_num; } } break; default: goto sp_arg; } } sp_arg: k = 0; switch (*format++) { start: case 'F': if (cur_arg >= argc) throw('StrPrint'); if (flags & PRINTF_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 = ListSub(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 & PRINTF_AUX_FORMAT_NUM)) aux_format_num = 1; while (aux_format_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 |= PRINTF_NEG; m = -m; } sp_out_dec: if (flags & PRINTF_AUX_FORMAT_NUM) { if (!len) len = 12; d = m; goto sp_out_eng; } if (flags & PRINTF_COMMA) { comma_format_count = comma_count = 3; do { tmp_buf[k++] = ModU64(&m, 10) + '0'; if (!m) break; if (!--comma_count) { tmp_buf[k++] = ','; comma_count = 3; } } while (k < TMP_BUF_LEN - SLOP); sp_out_comma_num: if (flags & PRINTF_NEG) i = 1; else i = 0; if (len < 0) len = 0; if (flags & PRINTF_TRUNCATE && k + i > len) k = len - i; if (k < 0) k = 0; if (flags & PRINTF_PAD_ZERO) { if (flags & PRINTF_NEG) SPutChar(_dst, '-', _buf); comma_count = (len - k - i + comma_format_count - comma_count + 1) % (comma_format_count + 1) + 1; for (; i < len - k; i++) { if (!--comma_count) { SPutChar(_dst, ',', _buf); comma_count = comma_format_count; if (++i >= len - k) break; } SPutChar(_dst, '0', _buf); } } else { for (; i < len - k; i++) SPutChar(_dst, CH_SPACE, _buf); if (flags & PRINTF_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 & PRINTF_NEG) i = 1; else i = 0; if (len < 0) len = 0; if (flags & PRINTF_TRUNCATE && k + i > len) k = len - i; if (k < 0) k = 0; if (flags & PRINTF_PAD_ZERO) { if (flags & PRINTF_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 & PRINTF_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 |= PRINTF_NEG; d = -d; } if (d == inf) { sp_out_inf: if (flags & PRINTF_NEG) i = 1; else i = 0; k = 1; if (len < 0) len = 0; if (flags & PRINTF_TRUNCATE && k + i > len) k = len - i; if (k < 0) k = 0; for (; i < len - k; i++) SPutChar(_dst, CH_SPACE, _buf); if (flags & PRINTF_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 & PRINTF_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 & PRINTF_COMMA) { comma_count = i & 3; while (i-- && k < TMP_BUF_LEN - SLOP) { if (i > 2 && !comma_count--) { tmp_buf[k++] = ','; comma_count = 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 & PRINTF_COMMA) { comma_count = 3; do { if (n) { n--; tmp_buf[k++] = '0'; } else tmp_buf[k++] = ModU64(&m, 10) + '0'; if (!m) break; if (!--comma_count) { tmp_buf[k++] = ','; comma_count = 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 |= PRINTF_TRUNCATE; if (cur_arg >= argc) throw('StrPrint'); d = argv[cur_arg++](F64); if (d < 0) { flags |= PRINTF_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 |= PRINTF_NEG_E; } else flags &= ~PRINTF_NEG_E; i = 3; do tmp_buf[k++] = ModU64(&n, 10) + '0'; while (n && i--); if (flags & PRINTF_NEG_E) tmp_buf[k++] = '-'; tmp_buf[k++] = 'e'; dec_len = len - k - 2; if (flags & PRINTF_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 |= PRINTF_TRUNCATE; if (cur_arg >= argc) throw('StrPrint'); d = argv[cur_arg++](F64); if (d < 0) { flags |= PRINTF_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 |= PRINTF_TRUNCATE; if (cur_arg >= argc) throw('StrPrint'); d = argv[cur_arg++](F64); if (d < 0) { flags |= PRINTF_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 |= PRINTF_NEG_E; } if (flags & PRINTF_AUX_FORMAT_NUM && -24 <= n <= 24) { if (flags & PRINTF_QUESTION) { if (flags & PRINTF_NEG_E) i = -n / 3; else i = n / 3; j = 0; } else { if (flags & PRINTF_NEG_E) j = -n - aux_format_num; else j = n - aux_format_num; d *= Pow10I64(j); i = aux_format_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 & PRINTF_DECIMAL)) { dec_len = len - k - 2; if (flags & PRINTF_NEG) dec_len--; if (j > 0) { if (flags & PRINTF_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 & PRINTF_NEG_E) tmp_buf[k++] = '-'; tmp_buf[k++] = 'e'; if (!dec_len) { dec_len = len - k - 2; if (flags & PRINTF_NEG) dec_len--; d1 = d + Pow10I64(-dec_len + 1) / 2; if (d1 >= 10) { dec_len--; if (d1 >= 100) dec_len--; } } } if (flags & PRINTF_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 & PRINTF_COMMA) { comma_format_count = comma_count = 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_count) { tmp_buf[k++] = ','; comma_count = 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 & PRINTF_COMMA) { comma_format_count = comma_count = 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_count) { tmp_buf[k++] = ','; comma_count = 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 & PRINTF_COMMA) { comma_format_count = comma_count = 4; do { tmp_buf[k++] = m & 1 + '0'; m >>= 1; if (!m) break; if (!--comma_count) { tmp_buf[k++] = ','; comma_count = 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 *format, ...) {//See StrPrintJoin(). return StrPrintJoin(dst, format, argc, argv); } U8 *CatPrint(U8 *_dst, U8 *format, ...) {//StrCat(). See StrPrintJoin(). U8 *dst = _dst; while (*dst) dst++; StrPrintJoin(dst, format, argc, argv); return _dst; } U0 Print(U8 *format, ...) {//Print("") Format Strings. See StrPrintJoin(). //Don't use this. See Print() shortcut. U8 *buf = StrPrintJoin(NULL, format, argc, argv); PutS(buf);//Don't use PutS(). See Print() shortcut. Free(buf); } U8 *MStrPrint(U8 *format, ...) {//MAlloc StrPrint. See StrPrintJoin(). U8 *res, *buf = StrPrintJoin(NULL, format, argc, argv); res = StrNew(buf); Free(buf); return res; } U0 PrintErr(U8 *format, ...) {//Print "Err:" and message in blinking red. U8 *buf = StrPrintJoin(NULL, format, argc, argv); GetOutOfDollar; "%,p . %,p . %,p . %,p " ST_ERR_ST "%s", Caller, Caller(2), Caller(3), Caller(4), buf; Free(buf); } U0 PrintWarn(U8 *format, ...) {//Print "Warn:" and message in blinking red. U8 *buf = StrPrintJoin(NULL, format, argc, argv); GetOutOfDollar; "%,p . %,p . %,p . %,p " ST_WARN_ST "%s", Caller, Caller(2), Caller(3), Caller(4), buf; Free(buf); }