Contributor: PETER SAWATZKI

{$A+,B-,F-,G+,I-,P-,Q-,R-,S-,T-,V-,X+,Y+}
Program H2Pas;
{ Program:   H2PAS
  Version:   1.21
  Purpose:   convert C header files to some kind of Pascal units

  Developer: Peter Sawatzki (ps) (c) 1993
             Buchenhof 3, 58091 Hagen, Germany
 CompuServe: 100031,3002

  revision history:
  date       version  author   modification
  11/03/93   1.00     ps       written
  05/10/94   1.10     ps       add EXEHDR import support
  06/29/94   1.2x     ps       minor modifications
}
Uses
  Objects,
  Strings;

Const
  Version = 'H2Pas v.1.21';
  H2PasIni= 'H2Pas.Ini';
  CRLF = #13#10;
  StdUses: pChar = 'Uses'+CRLF+
                   '  WinTypes,'+CRLF+
                   '  WinProcs;';
  HasImports: Boolean = False;
  WhichBlock: (Undefd, InConst, InType, InVar, InFunc) = Undefd;
Var
  DstName,
  Imports: String[67];

  Function WordCount(aStr, Delims: pChar): Integer;
  Var
    Count: Integer;
    EndStr: pChar;
  Begin
    EndStr:= StrEnd(aStr);
    Count:= 0;
    While aStr<=EndStr Do Begin
      While (aStr<=EndStr) And (StrScan(Delims, aStr[0])<>Nil) Do Inc(aStr);
      If aStr<=EndStr Then Inc(Count);
      While (aStr<=EndStr) And (StrScan(Delims, aStr[0])=Nil) Do Inc(aStr)
    End;
    WordCount:= Count
  End;

  Function WordPosition (aStr, Delims: pChar; No: Integer): pChar;
  Var
    Count: Integer;
    EndStr: pChar;
  Begin
    EndStr:= StrEnd(aStr);
    Count:= 0;
    WordPosition:= Nil;
    While (aStr<=EndStr) And (Count<>No) Do Begin
      While (aStr<=EndStr) And (StrScan(Delims, aStr[0])<>Nil) Do Inc(aStr);
      If aStr<=EndStr Then Inc(Count);
      If Count<>No Then
        While (aStr<=EndStr) And (StrScan(Delims, aStr[0])=Nil) Do Inc(aStr)
      Else
        WordPosition:= aStr
    End
  End;

  Function ExtractWord (aDst, aStr, Delims: pChar; No: Integer): pChar;
  Var
    EndStr: pChar;
  Begin
    ExtractWord:= aDst;
    aStr:= WordPosition(aStr, Delims, No);
    If Assigned(aStr) Then Begin
      EndStr:= StrEnd(aStr);
      While (aStr<=EndStr) And (StrScan(Delims, aStr[0])=Nil) Do Begin
        aDst[0]:= aStr[0];
        Inc(aStr);
        Inc(aDst)
      End
    End;
    aDst[0]:= #0
  End;

  Function Trim (aDst, aSrc: pChar): pChar;
  Var
    EndStr: pChar;
  Begin
    Trim:= aDst;
    If Not Assigned(aSrc) Or (aSrc[0]=#0) Then
      aDst[0]:= #0
    Else Begin
      EndStr:= StrEnd(aSrc);
      While (aSrcaDst) And (EndStr[0]<=' ') Do Begin
        EndStr[0]:= #0;
        Dec(EndStr)
      End
    End
  End;

  Function Pad (aDst, aSrc: pChar; Count: Integer): pChar;
  Begin
    Pad:= aDst;
    If aDst<>aSrc Then
      StrCopy(aDst, aSrc);
    Count:= Count-StrLen(aDst);
    aDst:= StrEnd(aDst);
    While Count>0 Do Begin
      aDst[0]:= ' ';
      Inc(aDst);
      Dec(Count)
    End;
    aDst[0]:= #0
  End;

Function StrIPos(Str1, Str2: PChar): PChar;
Var
  EndStr: pChar;
  Len: Integer;
Begin
  StrIPos:= Nil;
  EndStr:= StrEnd(Str1);
  Len:= StrLen(Str2);
  Repeat
    Str1:= StrScan(Str1, Str2[0]);
    If Str1=Nil Then Exit;
    If StrLIComp(Str1, Str2, Len)=0 Then Begin
      StrIPos:= Str1;
      Exit
    End;
    Inc(Str1)
  Until Str1>EndStr
End;

  Function JustFilename(PathName : string) : string;
  {-Return just the filename of a pathname}
  Var
    I: Word;
  Begin
    I:= Succ(Word(Length(PathName)));
    Repeat
      Dec(I);
    Until (PathName[I] in  ['\', ':', #0]) or (I = 0);
    JustFilename := Copy(PathName, Succ(I), 64);
  End;

  function JustName(PathName : string) : string;
    {-Return just the name (no extension, no path) of a pathname}
  var
    DotPos : Byte;
  begin
    PathName := JustFileName(PathName);
    DotPos := Pos('.', PathName);
    if DotPos > 0 then
      PathName := Copy(PathName, 1, DotPos-1);
    JustName := PathName;
  end;

  Function JustPath(aName: string): string;
  {-Return just the path of a filename}
  Var
    I: Word;
  Begin
    I:= Succ(Word(Length(aName)));
    Repeat
      Dec(I);
    Until (aName[I] in  ['\', ':', #0]) or (I = 0);
    JustPath:= Copy(aName, 1, I)
  End;

  Procedure Fatal (aMsg: pChar);
  Begin
    WriteLn(aMsg);
    Halt(255)
  End;

  Function GetLine (aDst: pChar; Var aFile: Text): pChar;
  Var
    aString: String;
    p,i: Integer;
  Begin
    {$i-}
    ReadLn(aFile, aString);
    If IoResult<>0 Then Fatal('Read error.');
    p:= Pos('//', aString);
    If p>0 Then Begin
      aString[p+1]:= '*';
      aString:= aString+' */'
    End;
    p:= Pos(#9, aString);
    While p>0 Do Begin
      aString[p]:= ' ';
      For i:= 1 To 7-((p-1) Mod 8) Do
        Insert(' ', aString, p);
      p:= Pos(#9, aString)
    End;
    GetLine:= StrPCopy(aDst, aString)
  End;

  Procedure OutLn (Var aFile: Text; OutStr: pChar);
  Var
    oc: Char;
  Begin
    While OutStr[0]<>#0 Do Begin
      oc:= OutStr[0];
      Case oc Of
        '/': If OutStr[1]='*' Then Begin
               oc:= '{';
               Inc(OutStr,1)
             End;
        '*': If OutStr[1]='/' Then Begin
               oc:= '}';
               Inc(OutStr)
             End
      End;
      Write(aFile, oc);
      If IoResult<>0 Then Fatal('Write error.');
      Inc(OutStr)
    End;
    Write(aFile,CRLF);
    If IoResult<>0 Then Fatal('Write error.')
  End;

Procedure HeaderInfo (Var aFile: Text);
Var
  aLine: Array[0..100] Of Char;
Begin
  WriteLn(aFile, '{ Unit: ',DstName);
  WriteLn(aFile, '  Version: 1.00');
  WriteLn(aFile, '  translated from file ',DstName,'.H');
  WriteLn(aFile, '  raw translation using '+Version+', (c) Peter Sawatzki');
  WriteLn(aFile, '  fine tuned by:');
  WriteLn(aFile, '    (fill in)');
  WriteLn(aFile, ' ');
  WriteLn(aFile, '  revision history:');
  WriteLn(aFile, '  Date:    Ver: Author: Mod:');
  WriteLn(aFile, '  xx/xx/94 1.00   ');
  WriteLn(aFile, '}');
  WriteLn(aFile, 'Unit ',DstName,';');
  WriteLn(aFile, 'Interface');
  If StrLen(StdUses)<>0 Then
    WriteLn(aFile, StdUses);
End;

{-the collection part}
Type
  pImportEntry = ^tImportEntry;
  tImportEntry = Record
    TheName,
    TheDLL,
    TheOrd: pChar
  End;
  pImportCollection = ^tImportCollection;
  tImportCollection = Object(tSortedCollection)
    Function KeyOf(Item: Pointer): Pointer; Virtual;
    Function Compare(Key1, Key2: Pointer): Integer; Virtual;
    Procedure FreeItem(Item: Pointer); Virtual;
  End;

  pTypeMap = ^tTypeMap;
  tTypeMap = Record
    F, T: pChar;
  End;
  pTypeMapCollection = ^tTypeMapCollection;
  tTypeMapCollection = Object(tSortedCollection)
    Function KeyOf(Item: Pointer): Pointer; Virtual;
    Function Compare(Key1, Key2: Pointer): Integer; Virtual;
    Procedure FreeItem(Item: Pointer); Virtual;
  End;

Function tImportCollection.KeyOf(Item: Pointer): Pointer;
Begin
  KeyOf:= pImportEntry(Item)^.TheName
End;

Function tImportCollection.Compare(Key1, Key2: Pointer): Integer;
Begin
  Compare:= StrIComp(Key1, Key2)
End;

Procedure TImportCollection.FreeItem(Item: Pointer);
Begin
  StrDispose(pImportEntry(Item)^.TheName);
  StrDispose(pImportEntry(Item)^.TheDLL);
  StrDispose(pImportEntry(Item)^.TheOrd);
  Dispose(pImportEntry(Item))
End;

Function tTypeMapCollection.KeyOf(Item: Pointer): Pointer;
Begin
  KeyOf:= pTypeMap(Item)^.F
End;

Function tTypeMapCollection.Compare(Key1, Key2: Pointer): Integer;
Begin
  Compare:= StrIComp(Key1, Key2)
End;

Procedure tTypeMapCollection.FreeItem(Item: Pointer);
Begin
  StrDispose(pTypeMap(Item)^.F);
  StrDispose(pTypeMap(Item)^.T);
  Dispose(pTypeMap(Item))
End;

Const
  TheImports: pImportCollection = Nil;
  TheFuncs: pStrCollection = Nil;
  TheStructs: pStrCollection = Nil;
  TheTypeMap: pTypeMapCollection = Nil;
  TheModMap: pStrCollection = Nil;

Procedure CreateCollections;
Begin
  TheImports:= New(pImportCollection, Init(100, 50));
  TheFuncs:= New(pStrCollection, Init(10, 20));
  TheStructs:= New(pStrCollection, Init(10, 20));
  TheTypeMap:= New(pTypeMapCollection, Init(10, 10));
  TheModMap:= New(pStrCollection, Init(10, 10));
End;

Procedure DestroyCollections;
Begin
  If Assigned(TheImports) Then Dispose(TheImports, Done);
  If Assigned(TheFuncs)   Then Dispose(TheFuncs,   Done);
  If Assigned(TheStructs) Then Dispose(TheStructs, Done);
  If Assigned(TheTypeMap) Then Dispose(TheTypeMap, Done);
  If Assigned(TheModMap)  Then Dispose(TheModMap,  Done);
End;

Procedure AddImport (aName, aDLL, anOrd: pChar);
Var
  anEntry: pImportEntry;
Begin
  anEntry:= New(pImportEntry);
  anEntry^.TheName:= StrNew(aName);
  anEntry^.TheDLL:= StrNew(aDLL);
  anEntry^.TheOrd:=  StrNew(anOrd);
  TheImports^.Insert(anEntry)
End;

Procedure AddFunc (aName: pChar);
Begin
  TheFuncs^.Insert(StrNew(aName))
End;

Procedure AddStruct (aName: pChar);
Begin
  TheStructs^.Insert(StrNew(aName))
End;

Procedure AddType (aSrc, aDst: pChar);
Var
  anEntry: pTypeMap;
Begin
  anEntry:= New(pTypeMap);
  anEntry^.F:= StrNew(aSrc);
  anEntry^.T:= StrNew(aDst);
  TheTypeMap^.Insert(anEntry)
End;

Procedure AddMod (aName: pChar);
Begin
  TheModMap^.Insert(StrNew(aName))
End;

Function GetOrdDLL (aName, RetDLL, RetOrd: pChar): Boolean;
Var
  Index: Integer;
Begin
  If TheImports^.Search(aName, Index) Then
    With pImportEntry(TheImports^.At(Index))^ Do Begin
      GetOrdDLL:= True;
      StrCopy(RetDLL, TheDLL);
      StrCopy(RetOrd, TheOrd)
    End
  Else
    GetOrdDLL:= False
End;

Procedure ReadImports (aFileName: String);
Var
  aFile: Text;
  aLine: Array[0..500] Of Char;
  aName,
  aDLL,
  anOrd: Array[0..60] Of Char;
  aWord: Array[0..60] Of Char;
Begin
  {$i-} Assign(aFile, aFileName); Reset(aFile);
  If IoResult<>0 Then Exit;
  HasImports:= True;
  StrCopy(aDLL, '?');
  While Not Eof(aFile) Do Begin
    GetLine(aLine, aFile);
    If StrComp(ExtractWord(aWord, aLine, ' ', 1),'Library:')=0 Then
      ExtractWord(aDLL, aLine, ' ', 2)
    Else
    If StrComp(ExtractWord(aWord, aLine, ' ', 5),'exported,')=0 Then Begin
      ExtractWord(anOrd, aLine, ' ', 1);
      ExtractWord(aName, aLine, ' ', 4);
      AddImport(aName, aDLL, anOrd)
    End
  End;
  Close(aFile)
End;

Procedure ReadIni;
Var
  IniFile: Text;
  aStr: String;
  aLine, Word1, Word2: Array[0..255] Of Char;
  rm: (rmNone, rmTypeMap, rmModMap);
  p: Integer;
Begin
  {$i-}
  Assign(IniFile, H2PasIni); Reset(IniFile);
  If IoResult<>0 Then Begin
    Assign(IniFile, JustPath(ParamStr(0))+'\'+H2PasIni);
    Reset(IniFile);
    If IoResult<>0 Then
      Exit
  End;
  rm:= rmNone;
  While Not Eof(IniFile) Do Begin
    ReadLn(IniFile, aStr);
    p:= Pos(';', aStr); If (p>0) Then aStr[0]:= Chr(p-1);
    StrPCopy(aLine, aStr); Trim(aLine, aLine);
    If StrLen(aLine)=0 Then
      Continue;
    If aLine[0]='[' Then Begin
      If StrIComp(aLine, '[TypeMap]')=0 Then rm:= rmTypeMap Else
      If StrIComp(aLine, '[ModMap]')=0 Then rm:= rmModMap Else
        rm:= rmNone;
      Continue
    End;
    Case rm Of
      rmTypeMap: AddType(Trim(Word1, ExtractWord(Word1, aLine, '=', 1)),
                         Trim(Word2, ExtractWord(Word2, aLine, '=', 2)));
      rmModMap:  AddMod(aLine);
    End
  End;
  Close(IniFile)
End;

Function Modifier (aPart: pChar): Boolean;
Var
  Index: Integer;
Begin
  Modifier:= TheModMap^.Search(aPart, Index)
End;

Function TypeConvert (aDst, aSrc: pChar): pChar;
Var
  aWord, ToParse: Array[0..79] Of Char;
  i, anInt, anError: Integer;
  aTemp: Array[0..79] Of Char;
  Index: Integer;
Begin
  TypeConvert:= aDst;
  aDst[0]:= #0;
  ExtractWord(aTemp, aSrc, '[]', 2);
  If StrLen(aTemp)>0 Then Begin
    Val(aTemp, anInt, anError);
    If anError=0 Then Begin
      Str(anInt-1:0, aTemp);
      StrCat(StrCat(StrCat(aDst,'Array[0..'), aTemp),'] Of ');
    End Else
      StrCat(StrCat(StrCat(aDst,'?'), aTemp),'?')
  End;
  ExtractWord(ToParse, aSrc, '[]', 1);
  aTemp[0]:= #0;
  For i:= 1 To WordCount(ToParse, ' ') Do Begin
    ExtractWord(aWord, ToParse, ' ', i);
    If aWord[0]='*' Then Begin
      StrCat(aTemp,'* ');
      aWord[0]:= ' ';
      Trim(aWord, aWord)
    End;
    If (aWord[0]<>#0) And Not Modifier(aWord) Then
      StrCat(StrCat(aTemp, aWord),' ');
  End;

  Trim(aTemp, aTemp);
  If TheTypeMap^.Search(@aTemp, Index) Then
    With pTypeMap(TheTypeMap^.At(Index))^ Do
      StrCopy(aTemp, T);
  StrCat(aDst, aTemp)
End;

Const
  IdMax = 50;
Type
  tIdTable = Array[1..IdMax] Of
    Record
      TheId,
      TheType: Array[0..79] Of Char;
      TheComment: Array[0..300] Of Char
    End;
Var
  IdCnt: Integer;
  IdTable: tIdTable;

  Procedure InitId;
  Begin
    IdCnt:= 0
  End;

  Procedure AddId (anId, aType, aComment: pChar);
  Begin
    If IdCnt=IdMax Then Begin
      WriteLn('Error: Id Table full. HALT.');
      Halt(1)
    End;
    Inc(IdCnt);
    With IdTable[IdCnt] Do Begin
      Trim(TheId, anId);
      TypeConvert(TheType, aType);
      Trim(TheComment, aComment)
    End
  End;

  Function ParseComment(Var Inf: Text; InStr, OutStr: pChar): Boolean;
  Var
    aWord: Array[0..40] Of Char;
  Begin
    ParseComment:= False;
    If StrPos(StrLCopy(aWord, InStr, 5),'/*')=Nil Then Exit;
    While StrPos(InStr, '*/')=Nil Do Begin
      StrCat(OutStr, InStr);
      GetLine(InStr, Inf)
    End;
    StrCat(OutStr, InStr);
    ParseComment:= True
  End;

  Function ParseDefine(InStr, OutStr: pChar): Boolean;
  Const
    DefineDelim = ' ';
  Var
    aWord: Array[0..512] Of Char;
    Rest, p: pChar;
    isConst: Boolean;
    i: Integer;
  Begin
    ParseDefine:= False;
    If WordCount(InStr, DefineDelim)<3 Then Exit;
    If  (ExtractWord(aWord, InStr, DefineDelim, 1)<>Nil)
    And (StrIComp(aWord, '#define')=0) Then Begin
      isConst:= False;
      If WhichBlock<>InConst Then
        StrCopy(OutStr,CRLF+'Const'+CRLF+'  ')
      Else
        StrCopy(OutStr,'  ');
      ExtractWord(StrEnd(OutStr), InStr, DefineDelim, 2);
      StrCat(Pad(OutStr, OutStr, 35), '= ');
      Rest:= WordPosition(InStr, DefineDelim, 3);
      StrCopy(aWord, Rest);
      p:= StrPos(aWord,'/*'); If Assigned(p) Then p^:= #0;
      Trim(aWord, aWord);
      If StrLen(aWord)>15 Then Exit;
      p:= StrPos(aWord, '0x');
      While Assigned(p) Do Begin
        isConst:= True;
        p[0]:= ' ';
        p[1]:= '$';
        p:= StrPos(p, '0x')
      End;
      p:= StrScan(aWord, 'L');  {get rid of the f*cking 'L'}
      While Assigned(p) Do Begin
        If (p>aWord) Then Begin
          Dec(p);
          If p^ In ['0'..'9','A'..'F','a'..'f'] Then Begin
            p[1]:= ' ';
            IsConst:= True
          End;
          Inc(p)
        End;
        p:= StrScan(p+1, 'L')
      End;
      If Not IsConst Then
        For i:= 0 To StrLen(aWord)-1 Do
          If aWord[i] In ['0'..'9'] Then Begin
            IsConst:= True;
            Break
          End;
      If Not IsConst Then
        Exit;
      Trim(aWord, aWord);
      StrCat(StrCat(OutStr, aWord), ';');
      p:= StrPos(Rest,'/*');
      If Assigned(p) Then
        StrCat(Pad(OutStr,OutStr, 60), p);
      WhichBlock:= InConst;
      ParseDefine:= True
    End
  End;

  Function ParseStruct(Var Inf: Text; InStr, OutStr: pChar): Boolean;
  Var
    aWord,
    aComment,
    RecComment,
    RecName,
    anId, aType,
    Rest: Array[0..300] Of Char;
    possibleArray: Array[0..60] Of Char;
    p, cp: pChar;
    i: Integer;
  Begin
    ParseStruct:= False;
    If  (StrIComp(ExtractWord(aWord, Instr, ' ', 1), 'struct')<>0)
    And (StrIComp(ExtractWord(aWord, Instr, ' ', 2), 'struct')<>0) Then
      Exit;
    p:= Instr;
    Instr:= StrScan(InStr, '{');
    If Not Assigned(InStr) Then Exit;

    {-try to parse the structure}
    InStr^:= #0;
    ExtractWord(RecName, p, ' ', WordCount(p,' '));
    Inc(InStr);
    Trim(InStr, InStr);
    If (InStr[0]='/') And (InStr[1]='*') Then
      StrCopy(RecComment, InStr)
    Else
      RecComment[0]:= #0;
    InStr:= StrEnd(InStr);
    cp:= InStr;
    Repeat
      GetLine(cp, Inf);
      p:= StrScan(cp, '}');
      cp:= StrEnd(cp);
      cp^:= ' '; Inc(cp); cp^:= #0
    Until Assigned(p);
    If WordCount(p+1,' ;')>0 Then
      ExtractWord(RecName, p+1, ' ;', 1);
    pChar(p-1)^:= #0;
    InitId;
    p:= InStr;
    Repeat
      cp:= p;
      p:= StrScan(p, ';');
      If Assigned(p) Then Begin
        Trim(aWord, ExtractWord(aWord, cp, ';', 1));
        {extract possible comment}
        cp:= StrPos(aWord, '/*');
        If Assigned(cp) Then Begin
          StrCopy(aComment, cp);
          cp^:= #0
        End Else
          aComment[0]:= #0;
        {-extract id and type}
        cp:= WordPosition(aWord, ' *', WordCount(aWord, ' *')); {last word}
        StrCopy(anId, cp);
        ExtractWord(possibleArray, anId,'[]',2);
        ExtractWord(anId, anId, '[]', 1);
        cp^:= #0;
        StrCopy(aType, aWord);
        If StrLen(possibleArray)>0 Then
          StrCat(StrCat(StrCat(aType,'['),possibleArray),']');
        {-extract comment if after ';'}
        Inc(p);
        While p^=' ' Do Inc(p);
        While (p[0]='/') And (p[1]='*') Do Begin
          {append comment}
          cp:= StrEnd(aComment);
          Repeat
            cp^:= p^;
            Inc(p);
            Inc(cp)
          Until (p[0]=#0) Or ((p[0]='*') And (p[1]='/'));
          cp[0]:= #0; StrCat(Trim(aComment, aComment),' */');
          If p[0]<>#0 Then
            Inc(p,2);
          While p^=' ' Do Inc(p)
        End;
        AddId(anId, aType, aComment)
      End
    Until Not Assigned(p);

    {-output the structure}
    If WhichBlock<>InType Then Begin
      StrCopy(OutStr,CRLF+'Type'+CRLF);
      OutStr:= StrEnd(OutStr)
    End;
    StrCopy(OutStr,'  ');
    StrCat(OutStr, RecName);
    StrCat(OutStr,' = Record');
    If RecComment[0]<>#0 Then
      StrCat(Pad(OutStr, OutStr, 40), RecComment);
    StrCat(OutStr,CRLF);
    For i:= 1 To IdCnt Do Begin
      OutStr:= StrEnd(OutStr);
      With IdTable[i] Do Begin
        StrCopy(OutStr,'    ');
        {If StrIComp(TheId, TheType)=0 Then StrCat(OutStr, '_');} {it works as is}
        StrCat(OutStr, TheId);
        If (i#0 Then Begin
          Pad(OutStr, OutStr, 40);
          StrCat(OutStr, TheComment)
        End;
        StrCat(OutStr,CRLF)
      End
    End;
    StrCat(OutStr,'  End;');
    AddStruct(RecName);
    WhichBlock:= InType;
    ParseStruct:= True
  End;

  Function ParseAPI(Var Inf: Text; InStr, OutStr: pChar): Boolean;
  Var
    FHead,
    aWord,
    Res,
    FuncComment,
    FuncName,
    anId, aType, aComment: Array[0..200] Of Char;
    p, cp, cp2, pStart: pChar;
    i, Indent: Integer;
    IsFunc: Boolean;
    Unknown: Integer;

    Function ParseWordAndComment (aComment, aWord, Src: pChar; Delim: Char): pChar;
    {parse Src, search for delim. append comments to aComment, source to aWord}
    Var
      cp: pChar;
    Begin
      Repeat
        While Src^=' ' Do Inc(Src);
        While (Src[0]='/') And (Src[1]='*') Do Begin
          {append comment}
          cp:= StrEnd(aComment);
          Repeat
            cp^:= Src^;
            Inc(Src);
            Inc(cp)
          Until (Src[0]=#0) Or ((Src[0]='*') And (Src[1]='/'));
          cp[0]:= #0; StrCat(Trim(aComment, aComment),' */');
          If Src[0]<>#0 Then
            Inc(Src,2);
          While Src^=' ' Do Inc(Src)
        End;
        cp:= StrEnd(aWord);
        While Not(Src^ In [#0,',','/']) Do Begin
          cp^:= Src^; Inc(Src); Inc(cp)
        End;
        cp^:= #0;
        If Src^=#0 Then Begin
          ParseWordAndComment:= Src;
          Exit
        End
      Until Src^=',';
      Inc(Src);
      While Src^=' ' Do Inc(Src);
      While (Src[0]='/') And (Src[1]='*') Do Begin
        {append comment}
        cp:= StrEnd(aComment);
        Repeat
          cp^:= Src^;
          Inc(Src);
          Inc(cp)
        Until (Src[0]=#0) Or ((Src[0]='*') And (Src[1]='/'));
        cp[0]:= #0; StrCat(Trim(aComment, aComment),' */');
        If Src[0]<>#0 Then
          Inc(Src,2);
        While Src^=' ' Do Inc(Src)
      End;
      ParseWordAndComment:= Src
    End;

  Begin
    ParseAPI:= False;
    IsFunc:= False;
    FuncName[0]:= #0;
    Res[0]:= #0;
    If (StrPos(InStr,'typedef')<>Nil)
    Or (StrPos(InStr,'#define')<>Nil)
    Or (StrPos(InStr,'#if')<>Nil)
    Or (StrPos(InStr,'#el')<>Nil) Then Exit;
    pStart:= StrScan(InStr, '(');
    If Not Assigned(pStart) Then Exit;
    pStart^:= #0;
    Trim(FuncName, ExtractWord(FuncName, InStr, ' ', WordCount(InStr, ' ')));
    cp:= WordPosition(InStr, ' ', WordCount(InStr, ' '));
    If Assigned(cp) Then Begin
      cp[0]:= #0;
      Trim(Res, TypeConvert(Res, InStr))
    End Else
      StrCopy(Res, '?????');
    InStr:= pStart+1;
    cp:= InStr;
    p:= StrScan(cp, ';');
    While Not Assigned(p) Do Begin
      cp:= StrEnd(cp);
      cp^:= ' '; Inc(cp);
      GetLine(cp, Inf);
      p:= StrScan(cp, ';')
    End;
    StrCopy(FuncComment, p+1);
    Repeat
      Dec(p)
    Until (p<=InStr) Or (p^=')');
    p^:= #0;

    InitId;
    Unknown:= 0;
    p:= InStr;
    While p^<>#0 Do Begin
      aComment[0]:= #0;
      aWord[0]:= #0;
      p:= ParseWordAndComment(aComment, aWord, p, ',');
      Trim(aWord, aWord);
      TypeConvert(aType, aWord);
      anId[0]:= #0;
      cp:= WordPosition(aWord, ' *', WordCount(aWord, ' *')); {last word}
      If (WordCount(aWord,' *')=1)
      Or (Assigned(cp) And (StrIComp(cp, TypeConvert(aType, cp))<>0)) Then Begin
      {non-Ansi declaration}
        Inc(Unknown);
        Str(Unknown, anId);
        Move(anId[0], anId[3], StrLen(anId)+1);
        anId[0]:= 'P'; anId[1]:= 'a'; anId[2]:= 'r';
      End Else Begin
        If Assigned(cp) Then Begin
          StrCopy(anId, cp);
          cp^:= #0
        End;
        TypeConvert(aType, aWord)
      End;
      AddId(anId, aType, aComment)
    End;

    StrCopy(OutStr, '  Function ');
    StrCat(OutStr, FuncName);
    StrCat(OutStr, ' (');
    Indent:= StrLen(OutStr);
    OutStr:= StrEnd(OutStr);
    aWord[0]:= #0;
    For i:= 1 To IdCnt Do
      With IdTable[i] Do Begin
        StrCat(aWord, TheId);
        If (i#0 Then
          StrCat(Pad(aWord, aWord, 60-Indent), TheComment);
        If (Indent+StrLen(aWord)>90) Or (TheComment[0]<>#0) Then Begin
          StrCopy(OutStr, aWord); OutStr:= StrEnd(OutStr);
          If i'+CRLF+
                '#include "'+DstName+'.h"'+CRLF+
                'void veri (char *aStr, int aSize)'+CRLF+
                '{ printf("Size of %s= %5i\n",aStr,aSize); }'+CRLF);
    WriteLn(Out,'void main (void)'+CRLF+
                '{ printf("verification of '+DstName+' for C:\n");');
    TheStructs^.ForEach(@VeriC);
    WriteLn(Out,'}');
  End;

Const
  LineBufSize = 5000;
  IoBufSize   = 32*1024;
Type
  IoBuf = Array[0..IoBufSize-1] Of Char;
  pIoBuf = ^IoBuf;
Var
  Inf, Out: Text;
  InStr,
  OutStr: pChar;
Begin
  WriteLn(Version,', written 1993 by P. Sawatzki');
  If Not (ParamCount In [2,3]) Then Begin
    WriteLn('Usage: H2Pas InFile OutFile [ImportList]');
    Halt
  End;
  CreateCollections;
  ReadIni;
  If ParamStr(3)<>'' Then
    Imports:= ParamStr(3)
  Else
    Imports:= JustName(ParamStr(1))+'.Imp';
  {$i-}
  Assign(Inf, ParamStr(1)); Reset(Inf);
  If IoResult<>0 Then Fatal('Input file not found');
  Assign(Out, ParamStr(2)); ReWrite(Out);
  If IoResult<>0 Then Fatal('Unable to create output file');
  DstName:= JustName(ParamStr(2));
  GetMem(InStr,  LineBufSize);
  GetMem(OutStr, LineBufSize);
  Write('Processing files...');
  HeaderInfo(Out);
  While Not Eof(Inf) Do Begin
    GetLine(InStr, Inf);
    OutStr[0]:= #0;
    If ParseComment(Inf, InStr, OutStr)
    Or ParseDefine(InStr, OutStr)
    Or ParseStruct(Inf, InStr, OutStr)
    Or ParseAPI(Inf, InStr, OutStr) Then
      OutLn(Out, OutStr)
    Else
      OutLn(Out, InStr)
  End;
  WriteLn('Done.');
  Write('Reading import file ',Imports,'...');
  ReadImports(Imports);
  If HasImports Then
    WriteLn('Done.')
  Else
    WriteLn('Not found.'+CRLF+
            '(generate an import file using "EXEHDR File.DLL >'+JustName(ParamStr(1))+
            '.Imp")');
  Write('Appending report...');
  GenerateReport(Out);
  WriteLn('Done.');
  DestroyCollections;
  FreeMem(InStr,  LineBufSize);
  FreeMem(OutStr, LineBufSize);
  Close(Inf);
  Close(Out)
End.

{ -------------  INFO ON THIS PROGRAM ------------------ }

ReadMe.Txt for H2Pas
====================

H2Pas is a quick and dirty hack to convert C-Header files to Pascal units.

If you make modifications, please drop me a copy at
  Peter Sawatzki, CompuServe 100031,3002

In it's current implementation (1.20) H2Pas does the following:

- convert structs
- convert constant defines
- convert procedure/function headers
- 'convert' comments of style /* xxxx */ to { xxxx }
  and comments of style // yyyy to { yyy }
- make use of IMPort files to resolve DLL index entries
- output C and Pascal code to verify correctness of C and Pascal
  structure sizes

How to use and generate import files:
-------------------------------------

if a EXEHDR type .IMP file is present for the DLL with information
about the entry points of a function, H2Pas outputs an unit implementation
section with entries of the form:

  Function Ctl3DEnabled;                  External 'CTL3D'    Index    5;

where the appropriate indices are resolved from information gathered
from the .IMP file.

To generate the .IMP file for a DLL -say CTL3D.DLL- one must do the following:

  EXEHDR CTL3D.DLL >CTL3D.IMP


How to execute H2Pas
--------------------

Usage:

H2Pas Ctl3D.H Ctl3D.Pas [Ctl3D.Imp]

where Ctl3D.H is the source C header file,
      Ctl3D.Pas is the destination pascal unit to be generated
  and Ctl3D.Imp is an optional import file generated from EXEHDR

H2Pas.Ini
---------

currently H2Pas.Ini has two areas for customization:

[TypeMap]
C-Type = Pascal-Type

maps a certain C-type to a Pascal type (see sample H2Pas.Ini)

[ModMap]
modifier

a list of modifiers that H2Pas should ignore (see sample H2Pas.Ini)

written by

  Peter Sawatzki
  Buchenhof 3
  58091 Hagen / Germany
  CompuServe: 100031,3002





 { ------------------  SAMPLE INI FILE NEED FOR THIS UNIT ---------- }
 { CUT and Save as H2PAS.INI                                         }

[TypeMap]
unsigned        = Word
unsigned int    = Word
char            = Char
unsigned long   = LongInt
int             = Integer
char far *      = pChar
unsigned char   = Byte
byte            = Byte
char *          = pChar
long            = LongInt
WORD            = Word
DWORD           = LongInt
ULONG           = LongInt
BOOL            = Bool
UINT            = Word
void *          = Pointer
; Windows stuff
BITMAPINFO      = tBitmapInfo
HANDLE          = tHandle
HWINDOW         = hWindow
COLORREF        = tColorRef

[ModMap]
WINAPI
WINGAPI
APIENTRY
EXPENTRY
EXPORT
EXTERN
PASCAL
FAR
_FAR
const