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 = " mænpfazy",
	*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)
{/*$LK,"Print(\"\") Format Strings",A="FI:::/Doc/Print.DD"$
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 == ì)
					{
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, 'ì', _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 == ì)
						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 == ì)
						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 == ì)
						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 $LK,"StrPrintJoin",A="MN:StrPrintJoin"$().
	return StrPrintJoin(dst, format, argc, argv);
}

U8 *CatPrint(U8 *_dst, U8 *format, ...)
{//StrCat().  See $LK,"StrPrintJoin",A="MN:StrPrintJoin"$().
	U8 *dst = _dst;

	while (*dst)
		dst++;
	StrPrintJoin(dst, format, argc, argv);

	return _dst;
}

U0 Print(U8 *format, ...)
{//$LK,"Print(\"\") Format Strings",A="FI:::/Doc/Print.DD"$.  See $LK,"StrPrintJoin",A="MN:StrPrintJoin"$().
//Don't use this.  $LK,"See Print() shortcut.",A="FF:::/Doc/CosmiC.DD,DemoCosmiC"$
	U8 *buf = StrPrintJoin(NULL, format, argc, argv);

	PutS(buf);//Don't use PutS().  $LK,"See Print() shortcut.",A="FF:::/Doc/CosmiC.DD,DemoCosmiC"$
	Free(buf);
}

U8 *MStrPrint(U8 *format, ...)
{//MAlloc StrPrint.  See $LK,"StrPrintJoin",A="MN: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);
}
