2
Copyright (c) 1999-2000 by Peter Vreman (msg2inc) and
3
Marco van de Voort (data2inc)
4
Placed under LGPL (See the file COPYING.FPC, included in this
5
distribution, for details about the copyright)
7
E-Mail Marco : Marcov@stack.nl
8
Homepage Marco: www.stack.nl/~marcov/xtdlib.htm
10
Data2Inc is a heavily modified version of msg2inc.pp which compiles the
11
inputfile to include files containing array of char( or byte) typed
14
(e.g. CONST xxx : ARRAY[0..xxx] OF CHAR =( aa,bb,cc,dd,ee); ,
15
or the same but ARRAY OF BYTE )
17
Two types of input file are allowed:
19
1 A special kind of textfile. Records start with '!'name and all following
20
non empty and non comment (starting with '#',':' or '%') lines until
21
the next line starting with '!' or EOF are the data. Data are either
22
plain text (with \xxx ordinal constants) lines or a kinbd of
23
Basic DATA command (these lines start with DATA).
24
See demo.txt included with this package for a commented example.
26
2 (special parameter -b)
27
An arbitrary binary file can get converted to constants. In this mode
28
only one constant per include file is possible.
30
This program is distributed in the hope that it will be useful,
31
but WITHOUT ANY WARRANTY; without even the implied warranty of
32
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
34
**********************************************************************}
41
maxbufsize = 1024*1024; { 1 mb buffer }
44
TOutputMode=(OutByte,OutChar,OutString);
47
{*****************************************************************************
48
Simple service routines. These are copied from EPasStr.
49
*****************************************************************************}
51
TYPE CHARSET=SET OF CHAR;
53
FUNCTION NextCharPos(CONST S : String;C:CHAR;Count:LONGINT):LONGINT;
69
WHILE (S[J]<>C) AND (J<=I) DO INC(J);
76
FUNCTION NextCharPosSet(CONST S : String;CONST C:CHARSET;Count:LONGINT):LONGINT;
92
WHILE (j<=i) AND (NOT (S[J] IN C)) DO INC(J);
100
PROCEDURE RTrim(VAR P : String;Ch:Char);
105
I:=ORD(P[0]); { Keeping length in local data eases optimalisations}
109
WHILE (P[J]=Ch) AND (J>0) DO DEC(J);
115
PROCEDURE UpperCase(VAR S : String);
123
IF (S[I]>CHR(96)) AND (S[I]<CHR(123)) THEN
124
S[I]:=CHR(ORD(S[I])-32);
127
PROCEDURE LTrim(VAR P : String;Ch:Char);
132
I:=ORD(P[0]); { Keeping length in local data eases optimalisations}
136
WHILE (P[J]=Ch) AND (J<=I) DO INC(J);
143
{*****************************************************************************
145
*****************************************************************************}
147
FUNCTION XlatString(Var S : String):BOOLEAN;
148
{replaces \xxx in string S with #x, and \\ with \ (escaped)
149
which can reduce size of string.
151
Returns false when an error in the line exists}
154
Function GetNumber(Position:LONGINT):LONGINT;
164
C:=ORD(S[Position+I]);
165
IF (C>47) AND (C<56) THEN
188
WHILE A<=Length(S) DO
198
Value:=GetNumber(A+1);
222
Inname, { Name of input file }
223
OutName, { Name of output (.inc) file }
224
BinConstName : string; { (-b only) commandline name of constant }
225
OutputMode : TOutputMode; { Output mode (char,byte,string) }
226
I_Binary : BOOLEAN; { TRUE is binary input, FALSE textual }
227
MsgTxt : pchar; { Temporary storage of data }
228
msgsize : longint; { Bytes used in MsgTxt }
232
{*****************************************************************************
234
*****************************************************************************}
236
{Dump the contents of MsgTxt (msgsize bytes) to file T (which has been opened),
237
using CONSTNAME as the name of the ARRAY OF CHAR constant}
238
procedure WriteCharFile(var t:text;constname:string);
240
function createconst(b:byte):string;
241
{decides whether to use the #xxx code or 'c' style for each char}
243
if (b in [32..127]) and (b<>39) then
244
createconst:=''''+chr(b)+''''
246
createconst:='#'+chr(b div 100+48)+chr((b mod 100) div 10+48)+chr(b mod 10+48)
253
Writeln('Writing constant: ',constname,' to file '#39,outname,#39);
255
write(t,'const ',constname,' : array[0..'); Writeln(t,msgsize-1,'] of char=(');
258
for i:=0 to msgsize-1 do
274
write(t,createconst(ord(p^)));
283
{*****************************************************************************
285
*****************************************************************************}
287
{Dump the contents of MsgTxt (msgsize bytes) to file T (which has been opened),
288
using CONSTNAME as the name of the ARRAY OF BYTE constant}
289
procedure WriteByteFile(var t:text;constname:string);
291
function createconst(b:byte):string;
292
{Translates byte B to a $xx hex constant}
295
createconst[1]:='$'; createconst[0]:=#3;
299
createconst[2]:=CHR(l);
300
l:=ORD(B and 15) +48;
303
createconst[3]:=CHR(l);
310
Writeln('Writing constant: ',constname,' to file '#39,outname,#39);
312
write(t,'const ',constname,' : array[0..'); Writeln(t,msgsize-1,'] of byte=(');
315
for i:=0 to msgsize-1 do
331
write(t,createconst(ord(p^)));
340
{*****************************************************************************
342
*****************************************************************************}
344
procedure WriteStringFile(var t:text;constname:string);
346
maxslen=240; { to overcome aligning problems }
348
function l0(l:longint):string;
353
while (length(s)<5) do
365
Writeln('Writing constant: ',constname,' to file '#39,outname,#39);
367
writeln(t,'{$ifdef Delphi}');
368
writeln(t,'const '+constname+' : array[0..',(msgsize-1) div maxslen,'] of string[',maxslen,']=(');
369
writeln(t,'{$else Delphi}');
370
writeln(t,'const '+constname+' : array[0..',(msgsize-1) div maxslen,',1..',maxslen,'] of char=(');
371
write(t,'{$endif Delphi}');
372
{Parse buffer in msgbuf and create indexs}
378
for i:=1 to msgsize do
380
if slen>=maxslen then
391
if (len>70) or (start) then
407
if (ord(p^)>=32) and (p^<>#39) then
426
write(t,'#'+chr(ord(p^) div 100+48)+chr((ord(p^) mod 100) div 10+48)+chr(ord(p^) mod 10+48));
429
{ start a new line when a #0 or #10 is found }
430
if p^ in [#0,#10] then
442
{*****************************************************************************
444
*****************************************************************************}
446
FUNCTION SpecialItem(S : String):LONGINT;
447
{ This procedure finds the next comma, (or the end of the string)
448
but comma's within single or double quotes should be ignored.
449
Single quotes within double quotes and vice versa are also ignored.}
451
VAR DataItem : LONGINT;
453
CONST xFcl : CHARSET = [',',#39,'"'];
459
DataItem:=NextCharPosSet(S,xFcl,DataItem+1); {Find first " ' or ,}
460
IF (DataItem<>0) AND ((S[DataItem]='"') OR (S[DataItem]=#39)) THEN { (double)Quote found?}
461
DataItem:=NextCharPos(S,S[DataItem],DataItem+1); { then find other one}
462
UNTIL (DataItem=0) OR (S[DataItem]=',');
463
IF DataItem=0 THEN {Last data field of this line?}
465
SpecialItem:=DataItem;
469
{ Handles reading and processing of a textual file}
473
Outfile : text; {in and output textfiles}
474
line, DataItem, {line number, position in DATA line}
475
I1,I2, {4 temporary counters}
477
s,S1 : string; {S is string after reading, S1 is temporary string or
478
current DATA-item being processed }
479
VarName : String; { Variable name of constant to be written}
481
PROCEDURE ParseError;
482
{Extremely simple errorhandler}
484
Writeln('Error in line : ',Line, ' Somewhere near :',#39,S1,#39);
485
Close(InfIle); Close(Outfile);
490
{ Reads decimal value starting at S1[1].
491
Value in I3, number of digits found in I1}
492
var I1,I2,i3 : longint;
496
WHILE ((S1[I1]>#47) AND (S1[I1]<#58)) AND (I1<=Length(S1)) DO
503
I3:=(I3*10)+ ORD(S1[I2])-48;
504
{Calc no of bytes(1,2 or 4) required from no of digits found}
508
IF (I1=3) AND (I3<256) THEN
515
IF (I1=5) AND (i3<65536) THEN
523
{ Reads a #xxx constant at S1[1], and puts it in msgtxt array.
524
Deletes #xxx constant from S1}
528
msgtxt[Msgsize]:=CHR(I3);
534
{ Reads a quoted text-string ('xxx' or "xxx"). Quotechar is in S1[1]
535
(always ' or "), any char except the quotechar is allowed between two
537
Deletes quoted textstring incl quotes from S1}
543
I1:=Pos(C,S1); {Find other quote}
545
ParseError; {Quotes have to be matched}
549
Move(S1[1],Msgtxt[Msgsize],I1);
556
PROCEDURE FixHex(base2:LONGINT);
557
{ Reads a base 2,8 or 16 constant from S1.
558
Parameter = 2Log of base (1,3 or 4 corresponding to base 2,8 and 16)
559
Constant is processed, the number of digits estimated (1,2 or 4 bytes) and
560
the value is appended to msgtxt accordingly}
564
WHILE (S1[I2] IN ['0'..'9','A'..'F','a'..'f']) AND (I2<=Length(S1)) DO
566
IF (S1[I2]>#47) AND (S1[I2]<#58) THEN
567
I3:=(I3 SHL base2)+ ORD(S1[I2])-48
569
IF (S1[I2]>#64) AND (S1[I2]<#71) THEN
570
I3:=(I3 SHL base2)+ ORD(S1[I2])-55
572
IF (S1[I2]>#96) AND (S1[I2]<#103) THEN
573
I3:=(I3 SHL base2)+ ORD(S1[I2])-87
586
3 : I4:=(I2*3 DIV 8)+1;
598
Writeln(' severe internal error ');
602
move(I3,msgtxt[Msgsize],i4);
607
{ processes aggregates of textual data like 'xxx'+#39"2143124"+'1234'#123}
613
'"',#39 : DoQuote; {Should I support octal codes here?}
624
PROCEDURE FlushMsgTxt; {Flush MsgTxt array}
626
IF msgsize>0 THEN {In memory? Then flush}
630
WriteByteFile(outfile,Varname);
632
WriteCharFile(outfile,varname);
634
WriteStringFile(outfile,varname);
642
Getmem(msgtxt,maxbufsize);
643
Writeln('processing file : ',inname);
644
{Read the message file}
645
assign(infile,inname);
651
WriteLn('file '+inname+' not found');
655
assign (outfile,outname);
659
while not eof(infile) do
661
readln(infile,s); {Read a line}
665
IF S1='DATA ' THEN {DATA keyword?}
669
DataItem:=SpecialItem(S); {Yes. Determine size of DATA field.}
673
IF DataItem=Length(S) THEN
674
INC(i1); {DataItem fix for last field}
675
S1:=Copy(S,1,I1-1); { copy field to S1}
676
Delete(S,1,I1); {Delete field from S}
680
CASE S1[1] OF {Select field type}
681
#39,'"','#' : DoTextual; { handles textual aggregates
682
e.g. #124"142"#123'sdgf''ads'}
683
'$' : BEGIN {Handle $xxxx hex codes}
691
'0'..'9' : BEGIN { handles 0x124,124124,124124H,234h,666o,353d,24b}
692
IF (Length(S1)>1) AND (S1[2]='x') THEN {C style 0xABCD hex}
697
ELSE {other types (HP notation suffix h,o,d and b (and upcase versions,
700
CASE S1[Length(S1)] OF
701
'H','h' : FixHex(4); {Hex}
702
'o','O' : FixHex(3); {octal}
703
'B','b' : BEGIN {Binary}
704
DEC(S1[0]); {avoid 'b' char being treated as
708
'0'..'9','d','D' : BEGIN {decimal versions}
709
FixDec; {Fixdec is safe for trailing chars}
710
{I1 =no of digits, I3=value, I2= no bytes needed}
711
move(I3,msgtxt[Msgsize],i2);
715
ParseError; {otherwise wrong suffix}
719
'%' : BEGIN {%101010 binary constants}
723
'\' : BEGIN {\xxx octal constants}
729
UNTIL {(DataItem:=Length(S)) OR} (DataItem=0); {parse until String is empty}
732
BEGIN {Non DATA line}
733
IF (Length(S)<>0) AND NOT (S[1] IN ['#',';','%']) THEN
736
IF NOT XlatString(S) THEN {Expand \xxx octal constants}
738
Writeln('Some error with a \xxx constant or a stale (unescaped) backslash');
741
IF C='!' THEN { New variable}
746
IF S[2]='$' THEN {Flag for ARRAY OF BYTE?}
757
move(s[1],msgtxt[Msgsize],i1);
764
FlushMsgTxt; {Flush variable if msgtxt is occupied}
769
{*****************************************************************************
771
*****************************************************************************}
779
Writeln('processing file : ',inname);
781
assign(infile,inname);
787
WriteLn('file '+inname+' not found');
790
{ First parse the file and count bytes needed }
791
msgsize:=FileSize(InFile);
792
Getmem(msgtxt,msgsize);
793
BlockRead(InFile,msgTxt[0],msgsize,i);
797
Writeln('Error while reading file',inName);
801
assign (outfile,outname);
805
WriteByteFile(outfile,BinconstName);
807
WriteCharFile(outfile,BinconstName);
809
WriteStringFile(outfile,BinconstName);
815
{*****************************************************************************
817
*****************************************************************************}
825
procedure helpscreen;
827
writeln('usage : data2inc [Options] <msgfile> [incfile] [constname]');
828
Writeln(' The constname parameter is only valid in combination');
829
writeln(' with -b, otherwise the constname must be specified in the inputfile');
831
writeln('<Options> can be :');
832
writeln(' -B File to read is binary.');
833
writeln(' -A array of byte output (default is array of char)');
834
writeln(' -S array of string output');
835
writeln(' -V Show version');
836
writeln(' -? or -H This HelpScreen');
838
Writeln(' See data2inc.exm for a demonstration source');
847
for i:=1to paramcount do
850
if (para[1]='-') then
855
'B' : I_Binary:=TRUE;
856
'A' : OutputMode:=OutByte;
857
'S' : OutputMode:=OutString;
859
Writeln('Data2Inc ',version,' (C) 1999 Peter Vreman and Marco van de Voort');
863
'?','H' : Helpscreen;
875
3 : BinConstName:=Para;
879
if (FIles<3) AND I_Binary then