123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- {
- Copyright (c) 2019 by Nikolay Nikolov
- C structure checker tool. Generates Pascal and C code that prints the
- size of each structure and the size and offset of each of its members.
- Useful for checking ABI compatibility of Pascal bindings to C libraries.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- ****************************************************************************
- }
- program h2paschk;
- (* Example input file:
- @Pascal uses p_ddraw;
- @Pascal begin
- @C #include <ddraw.h>
- @C #include <stdio.h>
- @C #include <stddef.h>
- @C #include <tchar.h>
- @C int _tmain(int argc, _TCHAR* argv[])
- @C {
- @record TDDARGB,DDARGB
- .blue
- .green
- .red
- .alpha
- @record TDDRGBA,DDRGBA
- .red
- .green
- .blue
- .alpha
- @TYPE size_t
- @CONSTANT O_RW
- @C return 0;
- @C }
- @Pascal end.
- *)
- { TODO: Currently the files, describing the records, must be created manually.
- However, it would be extremely useful, if h2pas could also create them
- automatically. }
- {$MODE objfpc}{$H+}
- uses
- SysUtils;
- type
- TLanguage = (lPascal, lC);
- TLanguages = set of TLanguage;
- TIdentifier = array [TLanguage] of string;
- const
- DefaultExtension: array [TLanguage] of string = ('pas', 'c');
- CommLangID = lPascal;
- type
- { TH2PasCheckerCodeGen }
- TH2PasCheckerCodeGen = class
- private
- FInFileName: string;
- FOpenLanguageOutputs: TLanguages;
- FLangOutput: array [TLanguage] of TextFile;
- FCurrentRecord: TIdentifier;
- procedure Error;
- procedure InitLangOutput(Lang: TLanguage);
- procedure DoneLangOutput(Lang: TLanguage);
- procedure DoneLangOutputs;
- procedure StartRecord(RecordID: TIdentifier);
- procedure HandleType(TypeID: TIdentifier);
- procedure HandleConstant(ConstantID: TIdentifier;PascalType,PascalHexStrSize,CType,CFormat : string);
- procedure HandleConstant(ConstantID: TIdentifier);
- procedure HandleConstantU8(ConstantID: TIdentifier);
- procedure HandleConstantU16(ConstantID: TIdentifier);
- procedure HandleConstantU32(ConstantID: TIdentifier);
- procedure HandleConstantU64(ConstantID: TIdentifier);
- procedure HandleSignedConstant(ConstantID: TIdentifier;PascalType,CType,CFormat : string);
- procedure HandleSignedConstant(ConstantID: TIdentifier);
- procedure HandleConstantS8(ConstantID: TIdentifier);
- procedure HandleConstantS16(ConstantID: TIdentifier);
- procedure HandleConstantS32(ConstantID: TIdentifier);
- procedure HandleConstantS64(ConstantID: TIdentifier);
- procedure HandleFloatConstant(ConstantID: TIdentifier);
- procedure ProcessField(FieldID: TIdentifier);
- public
- destructor Destroy; override;
- procedure ProcessH2PasChk(const InFileName: string);
- end;
- procedure TH2PasCheckerCodeGen.Error;
- begin
- raise Exception.Create('Error!');
- end;
- procedure TH2PasCheckerCodeGen.InitLangOutput(Lang: TLanguage);
- begin
- AssignFile(FLangOutput[Lang], ChangeFileExt(FInFileName, '.' + DefaultExtension[Lang]));
- ReWrite(FLangOutput[Lang]);
- Include(FOpenLanguageOutputs, Lang);
- end;
- procedure TH2PasCheckerCodeGen.DoneLangOutput(Lang: TLanguage);
- begin
- if Lang in FOpenLanguageOutputs then
- begin
- CloseFile(FLangOutput[Lang]);
- Exclude(FOpenLanguageOutputs, Lang);
- end;
- end;
- procedure TH2PasCheckerCodeGen.DoneLangOutputs;
- var
- Lang: TLanguage;
- begin
- for Lang in TLanguage do
- DoneLangOutput(Lang);
- end;
- procedure TH2PasCheckerCodeGen.HandleType(TypeID: TIdentifier);
- begin
- Writeln(FLangOutput[lPascal], ' Writeln(''SizeOf(', TypeID[CommLangID], ')='',SizeOf(', TypeID[lPascal], '));');
- Writeln(FLangOutput[lC], ' printf("SizeOf(', TypeID[CommLangID], ')=%lu\n",sizeof(', TypeID[lC], '));');
- end;
- procedure TH2PasCheckerCodeGen.HandleConstant(ConstantID: TIdentifier;PascalType,PascalHexStrSize,CType,CFormat : string);
- begin
- Writeln(FLangOutput[lPascal], ' Writeln(''Unsigned value Of(', ConstantID[CommLangID], ')=0x'',hexstr(',PascalType,'(', ConstantID[lPascal], '),',PascalHexStrSize,'));');
- Writeln(FLangOutput[lC], ' printf("Unsigned value Of(', ConstantID[CommLangID], ')=0x',CFormat,'\n",(',CType,') ', ConstantID[lC], ');');
- end;
- procedure TH2PasCheckerCodeGen.HandleSignedConstant(ConstantID: TIdentifier;PascalType,CType,CFormat : string);
- begin
- Writeln(FLangOutput[lPascal], ' Writeln(''Signed value Of(', ConstantID[CommLangID], ')='',',PascalType,'(', ConstantID[lPascal],'));');
- Writeln(FLangOutput[lC], ' printf("Signed value Of(', ConstantID[CommLangID], ')=',CFormat,'\n",(',CType,') ', ConstantID[lC],');');
- end;
- procedure TH2PasCheckerCodeGen.HandleFloatConstant(ConstantID: TIdentifier);
- begin
- Writeln(FLangOutput[lPascal], ' Writeln(''Value Of(', ConstantID[CommLangID], ')='',',ConstantID[lPascal],':25:25);');
- Writeln(FLangOutput[lC], ' printf("Value Of(', ConstantID[CommLangID], ')=%0.25f\n",',ConstantID[lC],');');
- end;
- procedure TH2PasCheckerCodeGen.HandleConstant(ConstantID: TIdentifier);
- begin
- HandleConstant(ConstantID,'qword','16','unsigned long long','%016llX');
- end;
- procedure TH2PasCheckerCodeGen.HandleConstantU8(ConstantID: TIdentifier);
- begin
- HandleConstant(ConstantID,'byte','2','unsigned char','%02X');
- end;
- procedure TH2PasCheckerCodeGen.HandleConstantU16(ConstantID: TIdentifier);
- begin
- HandleConstant(ConstantID,'word','4','unsigned short','%04X');
- end;
- procedure TH2PasCheckerCodeGen.HandleConstantU32(ConstantID: TIdentifier);
- begin
- HandleConstant(ConstantID,'dword','8','unsigned int','%08X');
- end;
- procedure TH2PasCheckerCodeGen.HandleConstantU64(ConstantID: TIdentifier);
- begin
- HandleConstant(ConstantID,'qword','16','unsigned int','%016llX');
- end;
- procedure TH2PasCheckerCodeGen.HandleSignedConstant(ConstantID: TIdentifier);
- begin
- HandleSignedConstant(ConstantID,'int64','signed long long','%lld');
- end;
- procedure TH2PasCheckerCodeGen.HandleConstantS8(ConstantID: TIdentifier);
- begin
- HandleSignedConstant(ConstantID,'int8','signed char','%d');
- end;
- procedure TH2PasCheckerCodeGen.HandleConstantS16(ConstantID: TIdentifier);
- begin
- HandleSignedConstant(ConstantID,'int16','signed short','%d');
- end;
- procedure TH2PasCheckerCodeGen.HandleConstantS32(ConstantID: TIdentifier);
- begin
- HandleSignedConstant(ConstantID,'int32','signed int','%d');
- end;
- procedure TH2PasCheckerCodeGen.HandleConstantS64(ConstantID: TIdentifier);
- begin
- HandleSignedConstant(ConstantID,'int64','unsigned int','%lld');
- end;
- procedure TH2PasCheckerCodeGen.StartRecord(RecordID: TIdentifier);
- begin
- FCurrentRecord := RecordID;
- HandleType(RecordID);
- end;
- procedure TH2PasCheckerCodeGen.ProcessField(FieldID: TIdentifier);
- begin
- Writeln(FLangOutput[lPascal], ' Writeln(''SizeOf(', FCurrentRecord[CommLangID], '.', FieldID[CommLangID], ')='',SizeOf(', FCurrentRecord[lPascal], '.', FieldID[lPascal], '));');
- Writeln(FLangOutput[lPascal], ' Writeln(''OffsetOf(', FCurrentRecord[CommLangID], ',', FieldID[CommLangID], ')='',PtrUInt(@', FCurrentRecord[lPascal], '(nil^).', FieldID[lPascal], '));');
- Writeln(FLangOutput[lC], ' printf("SizeOf(', FCurrentRecord[CommLangID], '.', FieldID[CommLangID], ')=%lu\n",sizeof(((', FCurrentRecord[lC], '*)0)->', FieldID[lC], '));');
- Writeln(FLangOutput[lC], ' printf("OffsetOf(', FCurrentRecord[CommLangID], ',', FieldID[CommLangID], ')=%lu\n",offsetof(', FCurrentRecord[lC], ',', FieldID[lC], '));');
- end;
- destructor TH2PasCheckerCodeGen.Destroy;
- begin
- DoneLangOutputs;
- inherited Destroy;
- end;
- procedure TH2PasCheckerCodeGen.ProcessH2PasChk(const InFileName: string);
- var
- InF: TextFile;
- InS: string;
- Command: string;
- I: Integer;
- ID: TIdentifier;
- Lang: TLanguage;
- procedure ReadID;
- begin
- if Pos(',', InS) >= 1 then
- begin
- for Lang in TLanguage do
- begin
- if Pos(',', InS) >= 1 then
- begin
- ID[Lang] := Copy(InS, 1, Pos(',', InS) - 1);
- Delete(InS, 1, Pos(',', InS));
- end
- else
- begin
- ID[Lang] := InS;
- InS := '';
- end;
- end;
- end
- else
- for Lang in TLanguage do
- ID[Lang] := InS;
- end;
- begin
- FInFileName := InFileName;
- AssignFile(InF, InFileName);
- Reset(InF);
- try
- InitLangOutput(lPascal);
- InitLangOutput(lC);
- while not EoF(InF) do
- begin
- ReadLn(InF, InS);
- InS := TrimLeft(InS);
- if Length(InS) > 1 then
- begin
- case InS[1] of
- '#':
- begin
- { skip comment; nothing to do... }
- end;
- '@':
- begin
- Command := '@';
- I := 2;
- while (Length(InS) >= I) and (UpCase(InS[I]) in ['A'..'Z']) do
- begin
- Command := Command + UpCase(InS[I]);
- Inc(I);
- end;
- Delete(InS, 1, Length(Command));
- if (Length(InS) >= 1) and (InS[1] = ' ') then
- Delete(InS, 1, 1);
- case Command of
- '@PASCAL':
- Writeln(FLangOutput[lPascal], InS);
- '@C':
- Writeln(FLangOutput[lC], InS);
- '@FLOATCONSTANT':
- begin
- ReadID;
- HandleFloatConstant(ID);
- end;
- '@CONSTANT':
- begin
- ReadID;
- HandleConstant(ID);
- end;
- '@CONSTANT_U8':
- begin
- ReadID;
- HandleConstantU8(ID);
- end;
- '@CONSTANT_U16':
- begin
- ReadID;
- HandleConstantU16(ID);
- end;
- '@CONSTANT_U32':
- begin
- ReadID;
- HandleConstantU32(ID);
- end;
- '@CONSTANT_U64':
- begin
- ReadID;
- HandleConstantU64(ID);
- end;
- '@CONSTANT_S':
- begin
- ReadID;
- HandleSignedConstant(ID);
- end;
- '@CONSTANT_S8':
- begin
- ReadID;
- HandleConstantS8(ID);
- end;
- '@CONSTANT_S16':
- begin
- ReadID;
- HandleConstantS16(ID);
- end;
- '@CONSTANT_S32':
- begin
- ReadID;
- HandleConstantU32(ID);
- end;
- '@CONSTANT_S64':
- begin
- ReadID;
- HandleConstantS64(ID);
- end;
- '@TYPE':
- begin
- ReadID;
- HandleType(ID);
- end;
- '@RECORD':
- begin
- ReadID;
- StartRecord(ID);
- end;
- else
- Error;
- end;
- end;
- '.':
- begin
- Delete(InS, 1, 1);
- ReadID;
- ProcessField(ID);
- end;
- end;
- end;
- end;
- finally
- CloseFile(InF);
- end;
- end;
- var
- P: TH2PasCheckerCodeGen;
- begin
- if ParamCount = 0 then
- begin
- Writeln('Usage: h2paschk <filename>');
- Halt;
- end;
- P := TH2PasCheckerCodeGen.Create;
- try
- P.ProcessH2PasChk(ParamStr(1));
- finally
- FreeAndNil(P);
- end;
- end.
|