de20698b5fe0ca95e813a744c7b4f2a49db09199
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE a_modes.inc}
19 interface
21 uses
25 // ////////////////////////////////////////////////////////////////////////// //
26 type
30 // ////////////////////////////////////////////////////////////////////////// //
31 type
33 public
38 private
41 public
44 public
53 // process one byte, return `true` if codepoint is ready
59 // ////////////////////////////////////////////////////////////////////////// //
64 // strips out name from `fn`, leaving trailing slash
67 // ends with '/' or '\'?
70 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
71 // will add slash to `path`, even if `fn` is empty!
74 // does filename have one of ".wad", ".pk3", ".zip" extensions?
77 // does filepath have ".XXX:\" in it?
80 // adds ".wad" extension if filename doesn't have one of ".wad", ".pk3", ".zip"
83 // convert number to strig with nice commas
91 // `true` if strings are equal; ignoring case for cp1251
98 // `pathname` will be modified if path is valid
99 // `lastIsDir` should be `true` if we are searching for directory
100 // nobody cares about shitdoze, so i'll use the same code path for it
103 // return fixed AnsiString or empty AnsiString
106 // they throws
110 // little endian
138 // big endian
168 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
170 {$ENDIF}
182 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
184 {$ENDIF}
195 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
197 {$ENDIF}
199 type
202 // returns formatted string if `writerCB` is `nil`, empty string otherwise
203 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
210 // returns string in single or double quotes
211 // single quotes supports only pascal-style '' for single quote char
212 // double quotes supports c-style escapes
213 // function will select quote mode automatically
217 type
219 private
220 //type PItemT = ^ItemT;
223 public
224 type
226 private
230 public
237 private
241 private
248 public
252 //WARNING! don't change list contents in `for ... in`!
262 public
274 implementation
276 uses
277 xstreams;
280 // ////////////////////////////////////////////////////////////////////////// //
282 begin
287 begin
292 begin
297 // ////////////////////////////////////////////////////////////////////////// //
299 begin
306 begin
312 begin
317 // ////////////////////////////////////////////////////////////////////////// //
319 begin
327 begin
334 begin
340 begin
347 begin
354 begin
360 begin
367 begin
373 begin
379 var
381 begin
383 begin
393 var
395 begin
397 begin
404 var
406 begin
408 begin
411 end
412 else
413 begin
419 // ////////////////////////////////////////////////////////////////////////// //
420 var
425 // ////////////////////////////////////////////////////////////////////////// //
426 const
428 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
429 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
430 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
431 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
432 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
433 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
434 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
436 );
440 var
442 begin
450 // ////////////////////////////////////////////////////////////////////////// //
451 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
452 // code points from invalid range will never be valid, this is the property of the state machine
453 const
454 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
456 // maps bytes to character classes
473 // maps a combination of a state of the automaton and a character class to a state
483 // ////////////////////////////////////////////////////////////////////////// //
484 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
490 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
492 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
495 var
497 begin
500 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
507 // ////////////////////////////////////////////////////////////////////////// //
509 begin
515 // ////////////////////////////////////////////////////////////////////////// //
517 var
520 begin
522 begin
524 begin
528 begin
531 exit;
539 var
543 begin
546 begin
548 end
550 begin
553 end
555 begin
559 end
561 begin
566 end
567 else
568 begin
573 begin
575 begin
577 begin
580 begin
582 begin
584 end
585 else
586 begin
590 exit;
597 // ////////////////////////////////////////////////////////////////////////// //
599 begin
604 begin
607 end
608 else
609 begin
618 // ////////////////////////////////////////////////////////////////////////// //
622 var
624 begin
627 begin
635 var
638 begin
641 begin
649 begin
652 end
654 begin
657 end
658 else
659 begin
666 var
669 begin
671 begin
679 // ////////////////////////////////////////////////////////////////////////// //
681 var
684 begin
687 begin
690 begin
692 exit;
702 var
705 begin
710 begin
722 var
725 begin
729 begin
732 begin
734 begin
736 end
737 else
738 begin
741 exit;
748 begin
755 // strips out name from `fn`, leaving trailing slash
757 var
760 begin
765 begin
774 // ends with '/' or '\'?
776 begin
778 begin
780 end
781 else
782 begin
788 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
789 // will add slash to `path`, even if `fn` is empty!
791 var
793 begin
797 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
799 begin
801 //FIXME: make this faster!
802 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
803 begin
812 var
814 begin
816 result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
821 begin
828 var
831 begin
835 begin
837 begin
840 begin
842 if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') or StrEquCI1251(s, '.dfz') then
843 begin
845 exit;
855 var
857 begin
861 begin
868 begin
870 begin
872 end
873 else
874 begin
876 begin
878 end
879 else
880 begin
892 begin
894 begin
896 end
897 else
898 begin
900 begin
902 end
903 else
904 begin
916 var
918 begin
927 var
930 begin
932 begin
934 begin
938 exit;
941 // nothing to do
946 // ////////////////////////////////////////////////////////////////////////// //
947 // utils
948 // `ch`: utf8 start
949 // -1: invalid utf8
951 begin
963 var
965 begin
969 begin
975 // check other sequence bytes
977 begin
987 // ////////////////////////////////////////////////////////////////////////// //
988 const
990 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
991 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
992 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
993 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
994 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
995 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
996 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
998 );
1002 var
1004 begin
1005 (* The following encodings are valid, except for the 5 and 6 byte
1006 * combinations:
1007 * 0xxxxxxx
1008 * 110xxxxx 10xxxxxx
1009 * 1110xxxx 10xxxxxx 10xxxxxx
1010 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1011 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1012 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1013 *)
1021 // mask out unused bits
1029 // now continue
1031 begin
1039 // done, try 1251
1041 // alas
1046 var
1048 begin
1052 begin
1063 // ////////////////////////////////////////////////////////////////////////// //
1064 // `pathname` will be modified if path is valid
1065 // `lastIsDir` should be `true` if we are searching for directory
1066 // nobody cares about shitdoze, so i'll use the same code path for it
1068 var
1076 begin
1081 begin
1082 // remove trailing slashes
1085 // extract name
1088 begin
1092 // remove trailing slashes again
1095 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1096 // try the easiest case first
1099 begin
1101 begin
1102 // i found her!
1105 continue;
1108 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1109 // alas, either not found, or invalid attributes
1111 try
1113 repeat
1115 begin
1116 // i found her!
1120 break;
1123 finally
1132 const fileExtensions: array [0..6] of AnsiString = ('.wad', '.dfzip', '.dfwad', '.pk3', '.pak', '.zip', '.dfz');
1135 var
1138 begin
1140 //writeln('findDiskWad00: fname=<', fname, '>');
1144 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1146 begin
1147 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1149 begin
1150 //writeln(' SKIP');
1151 continue;
1161 begin
1167 var
1169 begin
1172 begin
1173 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1180 {$IFDEF ENDIAN_LITTLE}
1181 begin
1184 {$ELSE}
1185 var
1187 begin
1190 begin
1196 {$ENDIF}
1199 {$IFDEF ENDIAN_LITTLE}
1200 var
1202 begin
1205 begin
1211 {$ELSE}
1212 begin
1215 {$ENDIF}
1218 begin
1223 var
1226 begin
1229 begin
1231 begin
1234 end
1235 else
1236 begin
1238 begin
1257 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1259 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1260 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1261 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1270 begin
1272 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1277 var
1279 begin
1284 begin
1292 {$IFDEF ENDIAN_LITTLE}
1293 begin
1296 {$ELSE}
1297 var
1299 begin
1302 begin
1308 {$ENDIF}
1311 {$IFDEF ENDIAN_LITTLE}
1312 var
1314 begin
1317 begin
1323 {$ELSE}
1324 begin
1327 {$ENDIF}
1348 // ////////////////////////////////////////////////////////////////////////// //
1349 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1350 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1351 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1352 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1353 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1354 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1355 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1356 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1357 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1358 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1359 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1360 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1361 {$ENDIF}
1363 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1364 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1365 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1366 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1367 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1368 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1369 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1370 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1371 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1372 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1373 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1374 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1375 {$ENDIF}
1377 function nclamp (v, a, b: Byte): Byte; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1378 function nclamp (v, a, b: ShortInt): ShortInt; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1379 function nclamp (v, a, b: Word): Word; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1380 function nclamp (v, a, b: SmallInt): SmallInt; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1381 function nclamp (v, a, b: LongWord): LongWord; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1382 function nclamp (v, a, b: LongInt): LongInt; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1383 function nclamp (v, a, b: Int64): Int64; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1384 function nclamp (v, a, b: UInt64): UInt64; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1385 function nclamp (v, a, b: Single): Single; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1386 function nclamp (v, a, b: Double): Double; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1387 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1388 function nclamp (v, a, b: Extended): Extended; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1389 {$ENDIF}
1391 // ////////////////////////////////////////////////////////////////////////// //
1392 {$IFDEF WINDOWS}
1393 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1394 {$ELSE}
1395 {$IFDEF GO32V2}
1396 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external;
1397 {$ELSE}
1398 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1399 {$ENDIF}
1400 {$ENDIF}
1403 (*
1404 procedure conwriter (constref buf; len: SizeUInt);
1405 var
1406 ss: ShortString;
1407 slen: Integer;
1408 b: PByte;
1409 begin
1410 if (len < 1) then exit;
1411 b := PByte(@buf);
1412 while (len > 0) do
1413 begin
1414 if (len > 255) then slen := 255 else slen := Integer(len);
1415 Move(b^, ss[1], len);
1416 ss[0] := AnsiChar(slen);
1417 write(ss);
1418 b += slen;
1419 len -= slen;
1420 end;
1421 end;
1422 *)
1425 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1426 const
1428 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1429 var
1445 var
1449 begin
1453 begin
1455 end
1456 else
1457 begin
1459 begin
1471 begin
1476 begin
1482 var
1484 begin
1490 var
1492 begin
1498 begin
1502 begin
1512 begin
1517 var
1520 begin
1522 begin
1526 end
1527 else
1528 begin
1533 repeat
1535 begin
1539 end
1540 else
1541 begin
1543 begin
1546 end
1558 var
1560 begin
1563 repeat
1565 begin
1569 end
1570 else
1571 begin
1573 begin
1576 end
1586 var
1588 begin
1590 begin
1598 var
1600 begin
1602 begin
1609 begin
1613 begin
1614 // print literal part
1617 // output literal part
1619 begin
1621 begin
1623 break;
1627 begin
1631 end
1632 else
1633 begin
1637 continue;
1639 // check if we have argument for this format string
1641 begin
1644 break;
1646 // skip percent
1650 // parse format; check for sign
1654 // parse width
1657 begin
1658 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1662 begin
1668 end
1669 else
1670 begin
1674 // parse precision
1677 begin
1680 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1683 begin
1690 // get format char
1694 // done parsing format, check for valid format chars
1695 if not (fmtch in ['s','u','d','x','X','p','f','g','c']) then begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1696 // now write formatted string
1699 begin
1700 if not (fmtch in ['s','u','d','x','X']) then begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1709 begin
1711 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1716 begin
1718 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1723 begin
1728 else
1729 begin
1732 break;
1738 begin
1745 begin
1750 else
1751 begin
1754 break;
1757 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1761 begin
1768 begin
1774 begin
1776 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1780 begin
1782 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1785 else
1786 begin
1789 break;
1793 begin
1801 begin
1810 begin
1818 else
1819 begin
1822 break;
1827 begin
1831 end
1832 else
1833 begin
1841 begin
1842 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
1848 begin
1849 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
1854 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
1856 begin
1861 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
1862 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
1863 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
1864 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
1866 begin
1871 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1876 begin
1878 begin
1880 begin
1885 end
1886 else
1887 begin
1890 end
1891 else
1892 begin
1900 begin
1905 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1909 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
1913 else
1914 begin
1917 break;
1925 (*
1926 var
1927 ss: ShortString;
1928 ls: AnsiString;
1929 i64: Int64 = -$A000000000;
1930 ui64: UInt64 = $A000000000;
1931 begin
1932 writef(conwriter, 'test int:<%s> bool:<%s:%02d:%c> bool:<%s:%02d:%c>; char:<%2s;%c;%d>!'#10, [42, true, true, true, false, false, false, 'A', 'A', 'A']);
1933 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
1934 ss := 'fuckit';
1935 ls := 'FUCKIT';
1936 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
1937 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
1938 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
1939 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
1940 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
1941 *)