home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 June / Chip_2002-06_cd1.bin / zkuste / delphi / kolekce / d6 / rxlibsetup.exe / {app} / units / BdeUtils.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2002-02-19  |  47.9 KB  |  1,756 lines

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 2001,2002 SGB Software          }
  6. {         Copyright (c) 1997, 1998 Fedor Koshevnikov,   }
  7. {                        Igor Pavluk and Serge Korolev  }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11.  
  12.  
  13. unit BdeUtils;
  14.  
  15. {$I RX.INC}
  16. {$W-,R-,B-,N+,P+}
  17.  
  18. interface
  19.  
  20. uses SysUtils, Windows, Bde, Registry, RTLConsts, Classes, DB, DBTables,
  21.   IniFiles, DBUtils;
  22.  
  23. type
  24. {$IFNDEF WIN32}
  25.   TLocateFilter = (lfTree, lfCallback);
  26. {$ENDIF}
  27.  
  28. {$IFNDEF RX_D3}
  29.   TBDEDataSet = TDataSet;
  30. {$ENDIF}
  31.  
  32. {$IFNDEF RX_D5}
  33.   TDatabaseLoginEvent = TLoginEvent;
  34. {$ENDIF}
  35.  
  36.   TDBLocate = class(TLocateObject)
  37.   private
  38. {$IFNDEF WIN32}
  39.     FFilterHandle: HDBIFilter;
  40.     FTree: PChar;
  41.     FTreeSize: Integer;
  42.     FFilterKind: TLocateFilter;
  43.     procedure ActivateFilter;
  44.     procedure DeactivateFilter;
  45.     procedure DropFilter;
  46.     procedure CheckFilterKind;
  47.     procedure ChangeBookmark;
  48.     procedure BuildFilterHeader(var Rec);
  49.     procedure BuildFilterTree;
  50.     procedure FreeTree;
  51.     function RecordFilter(RecBuf: Pointer; RecNo: Longint): Smallint;
  52.       {$IFDEF WIN32} stdcall; {$ENDIF}
  53. {$ELSE}
  54.     function LocateCallback: Boolean;
  55.     procedure RecordFilter(DataSet: TDataSet; var Accept: Boolean);
  56. {$ENDIF WIN32}
  57.   protected
  58. {$IFDEF WIN32}
  59.     function LocateFilter: Boolean; override;
  60. {$ELSE}
  61.     procedure ActiveChanged; override;
  62.     function LocateFilter: Boolean; override;
  63. {$ENDIF WIN32}
  64.     procedure CheckFieldType(Field: TField); override;
  65.     function LocateKey: Boolean; override;
  66.     function UseKey: Boolean; override;
  67.     function FilterApplicable: Boolean; override;
  68.   public
  69.     destructor Destroy; override;
  70.   end;
  71.  
  72. { TCloneDataset }
  73.  
  74.   TCloneDataset = class(TBDEDataSet)
  75.   private
  76.     FSourceHandle: HDBICur;
  77.     FReadOnly: Boolean;
  78.     procedure SetReadOnly(Value: Boolean);
  79.     procedure SetSourceHandle(ASourceHandle: HDBICur);
  80.   protected
  81.     function CreateHandle: HDBICur; override;
  82.   public
  83.     property SourceHandle: HDBICur read FSourceHandle write SetSourceHandle;
  84.   published
  85.     property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
  86.   end;
  87.  
  88. { TCloneDbDataset }
  89.  
  90.   TCloneDbDataset = class(TDBDataSet)
  91.   private
  92.     FSourceHandle: HDBICur;
  93.     FReadOnly: Boolean;
  94.     procedure SetReadOnly(Value: Boolean);
  95.     procedure SetSourceHandle(ASourceHandle: HDBICur);
  96.   protected
  97.     function CreateHandle: HDBICur; override;
  98.   public
  99.     procedure InitFromDataSet(Source: TDBDataSet; Reset: Boolean);
  100.     property SourceHandle: HDBICur read FSourceHandle write SetSourceHandle;
  101.   published
  102.     property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
  103.   end;
  104.  
  105. { TCloneTable }
  106.  
  107.   TCloneTable = class(TTable)
  108.   private
  109.     FSourceHandle: HDBICur;
  110.     FReadOnly: Boolean;
  111.     procedure SetReadOnly(Value: Boolean);
  112.     procedure SetSourceHandle(ASourceHandle: HDBICur);
  113.   protected
  114.     function CreateHandle: HDBICur; override;
  115.   public
  116.     procedure InitFromTable(SourceTable: TTable; Reset: Boolean);
  117.   published
  118.     property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
  119.   end;
  120.  
  121. { Utility routines }
  122.  
  123. function CreateDbLocate: TLocateObject;
  124. {$IFNDEF WIN32}
  125. function CheckOpen(Status: DBIResult): Boolean;
  126. {$ENDIF}
  127. procedure FetchAllRecords(DataSet: TBDEDataSet);
  128. function TransActive(Database: TDatabase): Boolean;
  129. function AsyncQrySupported(Database: TDatabase): Boolean;
  130. {$IFDEF WIN32}
  131. function GetQuoteChar(Database: TDatabase): string;
  132. {$ENDIF}
  133. procedure ExecuteQuery(const DbName, QueryText: string);
  134. procedure ExecuteQueryEx(const SessName, DbName, QueryText: string);
  135. procedure BdeTranslate(Locale: TLocale; Source, Dest: PChar; ToOem: Boolean);
  136. function FieldLogicMap(FldType: TFieldType): Integer;
  137. function FieldSubtypeMap(FldType: TFieldType): Integer;
  138. procedure ConvertStringToLogicType(Locale: TLocale; FldLogicType: Integer;
  139.   FldSize: Word; const FldName, Value: string; Buffer: Pointer);
  140. function GetAliasPath(const AliasName: string): string;
  141. function IsDirectory(const DatabaseName: string): Boolean;
  142. function GetBdeDirectory: string;
  143. function BdeErrorMsg(ErrorCode: DBIResult): string;
  144. function LoginToDatabase(Database: TDatabase; OnLogin: TDatabaseLoginEvent): Boolean;
  145. function DataSetFindValue(ADataSet: TBDEDataSet; const Value,
  146.   FieldName: string): Boolean;
  147. function DataSetFindLike(ADataSet: TBDEDataSet; const Value,
  148.   FieldName: string): Boolean;
  149. function DataSetRecNo(DataSet: TDataSet): Longint;
  150. function DataSetRecordCount(DataSet: TDataSet): Longint;
  151. function DataSetPositionStr(DataSet: TDataSet): string;
  152. procedure DataSetShowDeleted(DataSet: TBDEDataSet; Show: Boolean);
  153. function CurrentRecordDeleted(DataSet: TBDEDataSet): Boolean;
  154. function IsFilterApplicable(DataSet: TDataSet): Boolean;
  155. function IsBookmarkStable(DataSet: TBDEDataSet): Boolean;
  156. function BookmarksCompare(DataSet: TBDEDataSet; Bookmark1,
  157.   Bookmark2: TBookmark): Integer;
  158. function SetToBookmark(ADataSet: TDataSet; ABookmark: TBookmark): Boolean;
  159. procedure SetIndex(Table: TTable; const IndexFieldNames: string);
  160. procedure RestoreIndex(Table: TTable);
  161. procedure DeleteRange(Table: TTable; IndexFields: array of const;
  162.   FieldValues: array of const);
  163. procedure PackTable(Table: TTable);
  164. procedure ReindexTable(Table: TTable);
  165. procedure BdeFlushBuffers;
  166. function GetNativeHandle(Database: TDatabase; Buffer: Pointer;
  167.   BufSize: Integer): Pointer;
  168. procedure ToggleDebugLayer(Active: Boolean; const DebugFile: string);
  169. procedure DbNotSupported;
  170.  
  171. { Export/import DataSet routines }
  172.  
  173. procedure ExportDataSet(Source: TBDEDataSet; DestTable: TTable;
  174.   TableType: TTableType; const AsciiCharSet: string;
  175.   AsciiDelimited: Boolean; MaxRecordCount: Longint);
  176. procedure ExportDataSetEx(Source: TBDEDataSet; DestTable: TTable;
  177.   TableType: TTableType; const AsciiCharSet: string;
  178.   AsciiDelimited: Boolean; AsciiDelimiter, AsciiSeparator: Char;
  179.   MaxRecordCount: Longint);
  180. procedure ImportDataSet(Source: TBDEDataSet; DestTable: TTable;
  181.   MaxRecordCount: Longint; Mappings: TStrings; Mode: TBatchMode);
  182.  
  183. { ReportSmith initialization }
  184.  
  185. procedure InitRSRUN(Database: TDatabase; const ConName: string;
  186.   ConType: Integer; const ConServer: string);
  187.  
  188. implementation
  189.  
  190. uses Forms, Controls, Dialogs, Consts, DBConsts, RXDConst, VCLUtils,
  191.   FileUtil, AppUtils, rxStrUtils, MaxMin, {$IFNDEF WIN32} Str16, {$ENDIF}
  192.   {$IFDEF RX_D3} BDEConst, DBCommon, {$ENDIF} DateUtil;
  193.  
  194. { Utility routines }
  195.  
  196. {$IFDEF RX_D5}
  197. procedure DBError(Ident: Word);
  198. begin
  199.   DatabaseError(LoadStr(Ident));
  200. end;
  201. {$ENDIF}
  202.  
  203. function IsBookmarkStable(DataSet: TBDEDataSet): Boolean;
  204. var
  205.   Props: CURProps;
  206. begin
  207.   with DataSet do
  208.     Result := Active and (DbiGetCursorProps(Handle, Props) = DBIERR_NONE) and
  209.       Props.bBookMarkStable;
  210. end;
  211.  
  212. function SetToBookmark(ADataSet: TDataSet; ABookmark: TBookmark): Boolean;
  213. begin
  214.   Result := False;
  215. {$IFDEF RX_D3}
  216.   with ADataSet do
  217.     if Active and (ABookmark <> nil) and not (Bof and Eof) and
  218.       BookmarkValid(ABookmark) then
  219.     try
  220.       ADataSet.GotoBookmark(ABookmark);
  221.       Result := True;
  222.     except
  223.     end;
  224. {$ELSE}
  225.   with TBDEDataSet(ADataSet) do
  226.     if Active and (ABookmark <> nil) and not (Bof and Eof) then
  227.       if DbiSetToBookmark(Handle, ABookmark) = DBIERR_NONE then
  228.       try
  229.         Resync([rmExact, rmCenter]);
  230.         Result := True;
  231.       except
  232.       end;
  233. {$ENDIF}
  234. end;
  235.  
  236. function BookmarksCompare(DataSet: TBDEDataSet; Bookmark1, Bookmark2: TBookmark): Integer;
  237. const
  238.   RetCodes: array[Boolean, Boolean] of ShortInt =
  239.     ((2, CMPLess), (CMPGtr, CMPEql));
  240. begin
  241.   Result := RetCodes[Bookmark1 = nil, Bookmark2 = nil];
  242.   if Result = 2 then begin
  243.     Check(DbiCompareBookmarks(DataSet.Handle, Bookmark1, Bookmark2, 
  244.       {$IFDEF WIN32} Result)); {$ELSE} Word(Result))); {$ENDIF}
  245.     if Result = CMPKeyEql then Result := CMPEql;
  246.   end;
  247. end;
  248.  
  249. function DBGetIntProp(const Handle: Pointer; PropName: Longint): Longint;
  250. var
  251.   Length: Word;
  252.   Value: Longint;
  253. begin
  254.   Value := 0;
  255.   Check(DbiGetProp(HDBIObj(Handle), PropName, @Value, SizeOf(Value), Length));
  256.   Result := Value;
  257. end;
  258.  
  259. {$IFDEF WIN32}
  260. function GetQuoteChar(Database: TDatabase): string;
  261. {$IFNDEF RX_D3}
  262. const
  263.   dbQUOTECHAR = $0404000A;
  264. {$ENDIF}
  265. var
  266.   Q: Char;
  267.   Len: Word;
  268. begin
  269.   Result := '';
  270.   if Database.IsSQLBased then begin
  271.     Q := #0;
  272.     DbiGetProp(HDBIObj(Database.Handle), dbQUOTECHAR, @Q, SizeOf(Q), Len);
  273.     if Q <> #0 then Result := Q;
  274.   end
  275.   else Result := '"';
  276. end;
  277. {$ENDIF}
  278.  
  279. function AsyncQrySupported(Database: TDatabase): Boolean;
  280. begin
  281.   Result := False;
  282.   if Database.Connected then
  283.     if Database.IsSQLBased then
  284.       try
  285.         Result := BOOL(DBGetIntProp(Database.Handle, dbASYNCSUPPORT));
  286.       except
  287.       end
  288.     else Result := {$IFDEF WIN32} True {$ELSE} False {$ENDIF};
  289. end;
  290.  
  291. function FieldLogicMap(FldType: TFieldType): Integer;
  292. {$IFNDEF RX_D3}
  293. {$IFDEF VER80}
  294. const
  295.   FldTypeMap: array[TFieldType] of Integer = (
  296.     fldUNKNOWN, fldZSTRING, fldINT16, fldINT32, fldUINT16, fldBOOL,
  297.     fldFLOAT, fldFLOAT, fldBCD, fldDATE, fldTIME, fldTIMESTAMP, fldBYTES,
  298.     fldVARBYTES, fldBLOB, fldBLOB, fldBLOB);
  299. {$ELSE}
  300. const
  301.   FldTypeMap: array[TFieldType] of Integer = (
  302.     fldUNKNOWN, fldZSTRING, fldINT16, fldINT32, fldUINT16, fldBOOL,
  303.     fldFLOAT, fldFLOAT, fldBCD, fldDATE, fldTIME, fldTIMESTAMP, fldBYTES,
  304.     fldVARBYTES, fldINT32, fldBLOB, fldBLOB, fldBLOB, fldBLOB, fldBLOB,
  305.     fldBLOB, fldBLOB);
  306. {$ENDIF}
  307. {$ENDIF}
  308. begin
  309.   Result := FldTypeMap[FldType];
  310. end;
  311.  
  312. function FieldSubtypeMap(FldType: TFieldType): Integer;
  313. {$IFNDEF RX_D3}
  314. {$IFDEF VER80}
  315. const
  316.   FldSubtypeMap: array[TFieldType] of Integer = (
  317.     0, 0, 0, 0, 0, 0, 0, fldstMONEY, 0, 0, 0, 0, 0, 0, fldstBINARY,
  318.     fldstMEMO, fldstGRAPHIC);
  319. {$ELSE}
  320. const
  321.   FldSubtypeMap: array[TFieldType] of Integer = (
  322.     0, 0, 0, 0, 0, 0, 0, fldstMONEY, 0, 0, 0, 0, 0, 0, fldstAUTOINC,
  323.     fldstBINARY, fldstMEMO, fldstGRAPHIC, fldstFMTMEMO, fldstOLEOBJ,
  324.     fldstDBSOLEOBJ, fldstTYPEDBINARY);
  325. {$ENDIF}
  326. {$ENDIF}
  327. begin
  328.   Result := FldSubtypeMap[FldType];
  329. end;
  330.  
  331. {$IFNDEF WIN32}
  332. function CheckOpen(Status: DBIResult): Boolean;
  333. begin
  334.   case Status of
  335.     DBIERR_NONE:
  336.       Result := True;
  337.     DBIERR_NOTSUFFTABLERIGHTS:
  338.       begin
  339.         if not Session.GetPassword then DbiError(Status);
  340.         Result := False;
  341.       end;
  342.   else
  343.     DbiError(Status);
  344.   end;
  345. end;
  346. {$ENDIF}
  347.  
  348. { Routine for convert string to IDAPI logical field type }
  349.  
  350. procedure ConvertStringToLogicType(Locale: TLocale; FldLogicType: Integer;
  351.   FldSize: Word; const FldName, Value: string; Buffer: Pointer);
  352. var
  353.   Allocate: Boolean;
  354.   BCD: FMTBcd;
  355.   E: Integer;
  356.   L: Longint;
  357.   B: WordBool;
  358.   DateTime: TDateTime;
  359.   DtData: TDateTime;
  360.   D: Double absolute DtData;
  361.   Data: Longint absolute DtData;
  362. {$IFDEF WIN32}
  363.   TimeStamp: TTimeStamp;
  364. {$ENDIF}
  365. begin
  366.   if Buffer = nil then begin
  367.     Buffer := AllocMem(FldSize);
  368.     Allocate := Buffer <> nil;
  369.   end
  370.   else Allocate := False;
  371.   try
  372.     case FldLogicType of
  373.       fldZSTRING:
  374.         begin
  375.           AnsiToNative(Locale, Value, PChar(Buffer), FldSize);
  376.         end;
  377.       fldBYTES, fldVARBYTES:
  378.         begin
  379.           Move(Value[1], Buffer^, Min(Length(Value), FldSize));
  380.         end;
  381.       fldINT16, fldINT32, fldUINT16:
  382.         begin
  383.           if Value = '' then FillChar(Buffer^, FldSize, 0)
  384.           else begin
  385.             Val(Value, L, E);
  386.             if E <> 0 then
  387. {$IFDEF RX_D3}
  388.               DatabaseErrorFmt(SInvalidIntegerValue, [Value, FldName]);
  389. {$ELSE}
  390.               DBErrorFmt(SInvalidIntegerValue, [Value, FldName]);
  391. {$ENDIF}
  392.             Move(L, Buffer^, FldSize);
  393.           end;
  394.         end;
  395.       fldBOOL:
  396.         begin
  397.           L := Length(Value);
  398.           if L = 0 then B := False
  399.           else begin
  400.             if Value[1] in ['Y', 'y', 'T', 't', '1'] then B := True
  401.             else B := False;
  402.           end;
  403.           Move(B, Buffer^, SizeOf(WordBool));
  404.         end;
  405.       fldFLOAT, fldBCD:
  406.         begin
  407.           if Value = '' then FillChar(Buffer^, FldSize, 0)
  408.           else begin
  409.             D := StrToFloat(Value);
  410.             if FldLogicType <> fldBCD then Move(D, Buffer^, SizeOf(Double))
  411.             else begin
  412.               DbiBcdFromFloat(D, 32, FldSize, BCD);
  413.               Move(BCD, Buffer^, SizeOf(BCD));
  414.             end;
  415.           end;
  416.         end;
  417.       fldDATE, fldTIME, fldTIMESTAMP:
  418.         begin
  419.           if Value = '' then Data := Trunc(NullDate)
  420.           else begin
  421.             case FldLogicType of
  422.               fldDATE:
  423.                 begin
  424.                   DateTime := StrToDate(Value);
  425. {$IFDEF WIN32}
  426.                   TimeStamp := DateTimeToTimeStamp(DateTime);
  427.                   Data := TimeStamp.Date;
  428. {$ELSE}
  429.                   Data := Trunc(DateTime);
  430. {$ENDIF}
  431.                 end;
  432.               fldTIME:
  433.                 begin
  434.                   DateTime := StrToTime(Value);
  435. {$IFDEF WIN32}
  436.                   TimeStamp := DateTimeToTimeStamp(DateTime);
  437.                   Data := TimeStamp.Time;
  438. {$ELSE}
  439.                   Data := Round(Frac(DateTime) * MSecsPerDay);
  440. {$ENDIF}
  441.                 end;
  442.               fldTIMESTAMP:
  443.                 begin
  444.                   DateTime := StrToDateTime(Value);
  445. {$IFDEF WIN32}
  446.                   TimeStamp := DateTimeToTimeStamp(DateTime);
  447.                   D := TimeStampToMSecs(DateTimeToTimeStamp(DateTime));
  448. {$ELSE}
  449.                   DtData := DateTime * MSecsPerDay;
  450. {$ENDIF}
  451.                 end;
  452.             end;
  453.           end;
  454.           Move(D, Buffer^, FldSize);
  455.         end;
  456.       else DbiError(DBIERR_INVALIDFLDTYPE);
  457.     end;
  458.   finally
  459.     if Allocate then FreeMem(Buffer, FldSize);
  460.   end;
  461. end;
  462.  
  463. { Execute Query routine }
  464.  
  465. procedure ExecuteQueryEx(const SessName, DbName, QueryText: string);
  466. begin
  467.   with TQuery.Create(Application) do
  468.   try
  469.     DatabaseName := DbName;
  470. {$IFDEF WIN32}
  471.     SessionName := SessName;
  472. {$ENDIF}
  473.     SQL.Add(QueryText);
  474.     ExecSQL;
  475.   finally
  476.     Free;
  477.   end;
  478. end;
  479.  
  480. procedure ExecuteQuery(const DbName, QueryText: string);
  481. begin
  482.   ExecuteQueryEx('', DbName, QueryText);
  483. end;
  484.  
  485. { Database Login routine }
  486.  
  487. function LoginToDatabase(Database: TDatabase; OnLogin: TDatabaseLoginEvent): Boolean;
  488. var
  489.   EndLogin: Boolean;
  490. begin
  491.   Result := Database.Connected;
  492.   if Result then Exit;
  493.   Database.OnLogin := OnLogin;
  494.   EndLogin := True;
  495.   repeat
  496.     try
  497.       Database.Connected := True;
  498.       EndLogin := True;
  499.     except
  500.       on E: EDbEngineError do begin
  501.         EndLogin := (MessageDlg(E.Message + '. ' + LoadStr(SRetryLogin),
  502.           mtConfirmation, [mbYes, mbNo], 0) <> mrYes);
  503.       end;
  504.       on E: EDatabaseError do begin
  505.         { User select "Cancel" in login dialog }
  506.         MessageDlg(E.Message, mtError, [mbOk], 0);
  507.       end;
  508.       else raise;
  509.     end;
  510.   until EndLogin;
  511.   Result := Database.Connected;
  512. end;
  513.  
  514. { ReportSmith runtime initialization routine }
  515.  
  516. procedure InitRSRUN(Database: TDatabase; const ConName: string;
  517.   ConType: Integer; const ConServer: string);
  518. const
  519.   IniFileName = 'RPTSMITH.CON';
  520.   scConNames = 'ConnectNamesSection';
  521.   idConNames = 'ConnectNames';
  522.   idType = 'Type';
  523.   idServer = 'Server';
  524.   idSQLDataFilePath = 'Database';
  525.   idDataFilePath = 'DataFilePath';
  526.   idSQLUserID = 'USERID';
  527. var
  528.   ParamList: TStringList;
  529.   DBPath: string[127];
  530.   TempStr, AppConName: string[127];
  531.   UserName: string[30];
  532.   ExeName: string[12];
  533.   IniFile: TIniFile;
  534. begin
  535.   ParamList := TStringList.Create;
  536.   try
  537. {$IFDEF WIN32}
  538.     Database.Session.GetAliasParams(Database.AliasName, ParamList);
  539. {$ELSE}
  540.     Session.GetAliasParams(Database.AliasName, ParamList);
  541. {$ENDIF}
  542.     if Database.IsSQLBased then DBPath := ParamList.Values['SERVER NAME']
  543.     else DBPath := ParamList.Values['PATH'];
  544.     UserName := ParamList.Values['USER NAME'];
  545.   finally
  546.     ParamList.Free;
  547.   end;
  548.   AppConName := ConName;
  549.   if AppConName = '' then begin
  550.     ExeName := ExtractFileName(Application.ExeName);
  551.     AppConName := Copy(ExeName, 1, Pos('.', ExeName) - 1);
  552.   end;
  553.   IniFile := TIniFile.Create(IniFileName);
  554.   try
  555.     TempStr := IniFile.ReadString(scConNames, idConNames, '');
  556.     if Pos(AppConName, TempStr) = 0 then begin
  557.       if TempStr <> '' then TempStr := TempStr + ',';
  558.       IniFile.WriteString(scConNames, idConNames, TempStr + AppConName);
  559.     end;
  560.     IniFile.WriteInteger(AppConName, idType, ConType);
  561.     IniFile.WriteString(AppConName, idServer, ConServer);
  562.     if Database.IsSQLBased then begin
  563.       IniFile.WriteString(AppConName, idSQLDataFilePath, DBPath);
  564.       IniFile.WriteString(AppConName, idSQLUserID, UserName);
  565.     end
  566.     else IniFile.WriteString(AppConName, idDataFilePath, DBPath);
  567.   finally
  568.     IniFile.Free;
  569.   end;
  570. end;
  571.  
  572. { BDE aliases routines }
  573.  
  574. function IsDirectory(const DatabaseName: string): Boolean;
  575. var
  576.   I: Integer;
  577. begin
  578.   Result := True;
  579.   if (DatabaseName = '') then Exit;
  580.   I := 1;
  581.   while I <= Length(DatabaseName) do begin
  582. {$IFDEF RX_D3}
  583.     if DatabaseName[I] in LeadBytes then Inc(I) else
  584. {$ENDIF RX_D3}
  585.     if DatabaseName[I] in [':','\'] then Exit;
  586.     Inc(I);
  587.   end;
  588.   Result := False;
  589. end;
  590.  
  591. function GetAliasPath(const AliasName: string): string;
  592. var
  593.   SAlias: DBINAME;
  594.   Desc: DBDesc;
  595.   Params: TStrings;
  596. begin
  597.   Result := '';
  598.   StrPLCopy(SAlias, AliasName, SizeOf(SAlias) - 1);
  599.   AnsiToOem(SAlias, SAlias);
  600.   Check(DbiGetDatabaseDesc(SAlias, @Desc));
  601.   if StrIComp(Desc.szDbType, szCFGDBSTANDARD) = 0 then begin
  602.     OemToAnsi(Desc.szPhyName, Desc.szPhyName);
  603.     Result := StrPas(Desc.szPhyName);
  604.   end
  605.   else begin
  606.     Params := TStringList.Create;
  607.     try
  608. {$IFDEF WIN32}
  609.       Session.Active := True;
  610. {$ENDIF}
  611.       Session.GetAliasParams(AliasName, Params);
  612.       Result := Params.Values['SERVER NAME'];
  613.     finally
  614.       Params.Free;
  615.     end;
  616.   end;
  617. end;
  618.  
  619. { TCloneDataset }
  620.  
  621. procedure TCloneDataset.SetSourceHandle(ASourceHandle: HDBICur);
  622. begin
  623.   if ASourceHandle <> FSourceHandle then begin
  624.     Close;
  625.     FSourceHandle := ASourceHandle;
  626.     if FSourceHandle <> nil then Open;
  627.   end;
  628. end;
  629.  
  630. function TCloneDataset.CreateHandle: HDBICur;
  631. begin
  632.   Check(DbiCloneCursor(FSourceHandle, FReadOnly, False, Result));
  633. end;
  634.  
  635. procedure TCloneDataset.SetReadOnly(Value: Boolean);
  636. begin
  637.   CheckInactive;
  638.   FReadOnly := Value;
  639. end;
  640.  
  641. { TCloneDbDataset }
  642.  
  643. procedure TCloneDbDataset.InitFromDataSet(Source: TDBDataSet; Reset: Boolean);
  644. begin
  645.   with Source do begin
  646. {$IFDEF WIN32}
  647.     Self.SessionName := SessionName;
  648. {$ENDIF}
  649.     Self.DatabaseName := DatabaseName;
  650.     SetSourceHandle(Handle);
  651. {$IFDEF WIN32}
  652.     Self.Filter := Filter;
  653.     Self.OnFilterRecord := OnFilterRecord;
  654.     if not Reset then Self.Filtered := Filtered;
  655. {$ENDIF}
  656.   end;
  657.   if Reset then begin
  658. {$IFDEF WIN32}
  659.     Filtered := False;
  660. {$ENDIF}
  661.     First;
  662.   end;
  663. end;
  664.  
  665. procedure TCloneDbDataset.SetSourceHandle(ASourceHandle: HDBICur);
  666. begin
  667.   if ASourceHandle <> FSourceHandle then begin
  668.     Close;
  669.     FSourceHandle := ASourceHandle;
  670.     if FSourceHandle <> nil then Open;
  671.   end;
  672. end;
  673.  
  674. function TCloneDbDataset.CreateHandle: HDBICur;
  675. begin
  676.   Check(DbiCloneCursor(FSourceHandle, FReadOnly, False, Result));
  677. end;
  678.  
  679. procedure TCloneDbDataset.SetReadOnly(Value: Boolean);
  680. begin
  681.   CheckInactive;
  682.   FReadOnly := Value;
  683. end;
  684.  
  685. { TCloneTable }
  686.  
  687. procedure TCloneTable.InitFromTable(SourceTable: TTable; Reset: Boolean);
  688. begin
  689.   with SourceTable do begin
  690.     Self.TableType := TableType;
  691.     Self.TableName := TableName;
  692. {$IFDEF WIN32}
  693.     Self.SessionName := SessionName;
  694. {$ENDIF}
  695.     Self.DatabaseName := DatabaseName;
  696.     if not Reset then begin
  697.       if IndexName <> '' then
  698.         Self.IndexName := IndexName
  699.       else if IndexFieldNames <> '' then
  700.         Self.IndexFieldNames := IndexFieldNames;
  701.     end;
  702.     SetSourceHandle(Handle);
  703. {$IFDEF WIN32}
  704.     Self.Filter := Filter;
  705.     Self.OnFilterRecord := OnFilterRecord;
  706.     if not Reset then Self.Filtered := Filtered;
  707. {$ENDIF}
  708.   end;
  709.   if Reset then begin
  710. {$IFDEF WIN32}
  711.     Filtered := False;
  712. {$ENDIF}
  713.     DbiResetRange(Handle);
  714.     IndexName := '';
  715.     IndexFieldNames := '';
  716.     First;
  717.   end;
  718. end;
  719.  
  720. procedure TCloneTable.SetSourceHandle(ASourceHandle: HDBICur);
  721. begin
  722.   if ASourceHandle <> FSourceHandle then begin
  723.     Close;
  724.     FSourceHandle := ASourceHandle;
  725.     if FSourceHandle <> nil then Open;
  726.   end;
  727. end;
  728.  
  729. procedure TCloneTable.SetReadOnly(Value: Boolean);
  730. begin
  731.   CheckInactive;
  732.   FReadOnly := Value;
  733. end;
  734.  
  735. function TCloneTable.CreateHandle: HDBICur;
  736. begin
  737.   Check(DbiCloneCursor(FSourceHandle, FReadOnly, False, Result));
  738. end;
  739.  
  740. { TDBLocate }
  741.  
  742. function CreateDbLocate: TLocateObject;
  743. begin
  744.   Result := TDBLocate.Create;
  745. end;
  746.  
  747. {$IFNDEF WIN32}
  748. function CallbackFilter(pDBLocate: Longint; RecBuf: Pointer;
  749.   RecNo: Longint): Smallint;
  750.   {$IFDEF WIN32} stdcall; {$ELSE} export; {$ENDIF WIN32}
  751. begin
  752.   Result := TDBLocate(pDBLocate).RecordFilter(RecBuf, RecNo);
  753. end;
  754. {$ENDIF WIN32}
  755.  
  756. destructor TDBLocate.Destroy;
  757. begin
  758. {$IFNDEF WIN32}
  759.   DropFilter;
  760. {$ENDIF}
  761.   inherited Destroy;
  762. end;
  763.  
  764. procedure TDBLocate.CheckFieldType(Field: TField);
  765. var
  766.   Locale: TLocale;
  767. begin
  768.   if not (Field.DataType in [ftDate, ftTime, ftDateTime]) then begin
  769.     if DataSet is TBDEDataSet then Locale := TBDEDataSet(DataSet).Locale
  770.     else Locale := Session.Locale;
  771.     ConvertStringToLogicType(Locale, FieldLogicMap(Field.DataType),
  772.       Field.DataSize, Field.FieldName, LookupValue, nil);
  773.   end;
  774. end;
  775.  
  776. function TDBLocate.UseKey: Boolean;
  777. var
  778.   I: Integer;
  779. begin
  780.   Result := False;
  781.   if DataSet is TTable then
  782.     with DataSet as TTable do begin
  783.       if (not Self.LookupField.IsIndexField) and (not IndexSwitch or
  784.         (not CaseSensitive and Database.IsSQLBased)) then Exit;
  785.       if (not LookupExact) and (Self.LookupField.DataType <> ftString) then Exit;
  786.       IndexDefs.Update;
  787.       for I := 0 to IndexDefs.Count - 1 do
  788.         with IndexDefs[I] do
  789.           if not (ixExpression in Options) and
  790.             ((ixCaseInsensitive in Options) or CaseSensitive) then
  791.             if AnsiCompareText(Fields, Self.LookupField.FieldName) = 0 then
  792.             begin
  793.               Result := True;
  794.               Exit;
  795.             end;
  796.     end;
  797. end;
  798.  
  799. function TDBLocate.LocateKey: Boolean;
  800. var
  801.   Clone: TCloneTable;
  802.  
  803.   function LocateIndex(Table: TTable): Boolean;
  804.   begin
  805.     with Table do begin
  806.       SetKey;
  807.       FieldByName(Self.LookupField.FieldName).AsString := LookupValue;
  808.       if LookupExact then Result := GotoKey
  809.       else begin
  810.         GotoNearest;
  811.         Result := MatchesLookup(FieldByName(Self.LookupField.FieldName));
  812.       end;
  813.     end;
  814.   end;
  815.  
  816. begin
  817.   try
  818.     TTable(DataSet).CheckBrowseMode;
  819.     if TTable(DataSet).IndexFieldNames = LookupField.FieldName then
  820.       Result := LocateIndex(TTable(DataSet))
  821.     else begin
  822.       Clone := TCloneTable.Create(DataSet);
  823.       with Clone do
  824.       try
  825.         ReadOnly := True;
  826.         InitFromTable(TTable(DataSet), True);
  827.         IndexFieldNames := Self.LookupField.FieldName;
  828.         Result := LocateIndex(Clone);
  829.         if Result then begin
  830.           Check(DbiSetToCursor(TTable(DataSet).Handle, Handle));
  831.           DataSet.Resync([rmExact, rmCenter]);
  832.         end;
  833.       finally
  834.         Free;
  835.       end;
  836.     end;
  837.   except
  838.     Result := False;
  839.   end;
  840. end;
  841.  
  842. function TDBLocate.FilterApplicable: Boolean;
  843. begin
  844.   Result := IsFilterApplicable(DataSet);
  845. end;
  846.  
  847. {$IFDEF WIN32}
  848.  
  849. function TDBLocate.LocateCallback: Boolean;
  850. var
  851.   Clone: TCloneDbDataset;
  852. begin
  853.   Result := False;
  854.   try
  855.     TBDEDataSet(DataSet).CheckBrowseMode;
  856.     Clone := TCloneDbDataset.Create(DataSet);
  857.     with Clone do
  858.     try
  859.       ReadOnly := True;
  860.       InitFromDataset(TDBDataSet(DataSet), True);
  861.       OnFilterRecord := RecordFilter;
  862.       Filtered := True;
  863.       if not (BOF and EOF) then begin
  864.         First;
  865.         Result := True;
  866.       end;
  867.       if Result then begin
  868.         Check(DbiSetToCursor(TBDEDataSet(DataSet).Handle, Handle));
  869.         DataSet.Resync([rmExact, rmCenter]);
  870.       end;
  871.     finally
  872.       Free;
  873.     end;
  874.   except
  875.     Result := False;
  876.   end;
  877. end;
  878.  
  879. procedure TDBLocate.RecordFilter(DataSet: TDataSet; var Accept: Boolean);
  880. begin
  881.   Accept := MatchesLookup(DataSet.FieldByName(LookupField.FieldName));
  882. end;
  883.  
  884. function TDBLocate.LocateFilter: Boolean;
  885. var
  886.   SaveCursor: TCursor;
  887. begin
  888.   if LookupExact or (LookupField.DataType = ftString) or
  889.     not (DataSet is TDBDataSet) then
  890.     Result := inherited LocateFilter
  891.   else begin
  892.     SaveCursor := Screen.Cursor;
  893.     Screen.Cursor := crHourGlass;
  894.     try
  895.       Result := LocateCallback;
  896.     finally
  897.       Screen.Cursor := SaveCursor;
  898.     end;
  899.   end;
  900. end;
  901.  
  902. {$ELSE WIN32}
  903.  
  904. type
  905.   TFilterRec = record { the simple filter tree with one condition }
  906.     Header: CANExpr;
  907.     Condition: CANBinary;
  908.     FieldNode: CANField;
  909.     ConstNode: CANConst;
  910.   end;
  911.  
  912. function TDBLocate.LocateFilter: Boolean;
  913. var
  914.   SaveCursor: TCursor;
  915.   Status: DBIResult;
  916. begin
  917.   SaveCursor := Screen.Cursor;
  918.   Screen.Cursor := crHourGlass;
  919.   try
  920.     ActivateFilter;
  921.     try
  922.       Check(DbiSetToBegin(TBDEDataSet(DataSet).Handle));
  923.       Status := DbiGetNextRecord(TBDEDataSet(DataSet).Handle, dbiNoLock,
  924.         nil, nil);
  925.       if Status = DBIERR_NONE then begin
  926.         DataSet.Resync([rmExact, rmCenter]);
  927.         ChangeBookmark;
  928.         Result := True;
  929.       end
  930.       else Result := False;
  931.     finally
  932.       DeactivateFilter;
  933.       if Result then SetToBookmark(DataSet, Bookmark);
  934.     end;
  935.   finally
  936.     Screen.Cursor := SaveCursor;
  937.   end;
  938. end;
  939.  
  940. procedure TDBLocate.BuildFilterHeader(var Rec);
  941. const
  942.   FCondition: array[Boolean] of CANOp = (canGE, canEQ);
  943.   FilterHeaderSize = SizeOf(CANExpr) + SizeOf(CANBinary) +
  944.     SizeOf(CANField) + SizeOf(CANConst);
  945. begin
  946.   with TFilterRec(Rec) do begin
  947.     with Header do begin
  948.       iVer := CANEXPRVERSION;
  949.       iNodes := 3;
  950.       iNodeStart := SizeOf(CANExpr);
  951.       iLiteralStart := FilterHeaderSize;
  952.     end;
  953.     with Condition do begin
  954.       nodeClass := nodeBINARY;
  955.       canOp := FCondition[LookupExact];
  956.       iOperand1 := SizeOf(CANBinary);
  957.       iOperand2 := iOperand1 + SizeOf(CANField);
  958.     end;
  959.     with FieldNode do begin
  960.       nodeClass := nodeFIELD;
  961.       canOp := canFIELD2;
  962.       iFieldNum := LookupField.FieldNo;
  963.       iNameOffset := 0;
  964.     end;
  965.     with ConstNode do begin
  966.       canOp := canCONST2;
  967.       iType := FieldLogicMap(LookupField.DataType);
  968.       iSize := LookupField.DataSize;
  969.       iOffset := Length(LookupField.FieldName) + 1;
  970.     end;
  971.     Header.iTotalSize := FilterHeaderSize + ConstNode.iSize +
  972.       ConstNode.iOffset;
  973.   end;
  974. end;
  975.  
  976. procedure TDBLocate.BuildFilterTree;
  977. var
  978.   Temp: PChar;
  979.   Rec: TFilterRec;
  980. begin
  981.   if FTree <> nil then FreeMem(FTree, FTreeSize);
  982.   FTree := nil;
  983.   BuildFilterHeader(Rec);
  984.   FTreeSize := Rec.Header.iTotalSize;
  985.   FTree := AllocMem(FTreeSize);
  986.   try
  987.     FillChar(FTree^, FTreeSize, 0);
  988.     Temp := FTree;
  989.     Move(Rec, FTree^, SizeOf(TFilterRec));
  990.     Inc(Temp, SizeOf(TFilterRec));
  991.     StrPCopy(PChar(Temp), LookupField.FieldName);
  992.     Inc(Temp, Rec.ConstNode.iOffset);
  993.     ConvertStringToLogicType(DataSet.Locale, FieldLogicMap(LookupField.DataType),
  994.       LookupField.DataSize, LookupField.FieldName, LookupValue, Temp);
  995.   except
  996.     FreeTree;
  997.     raise;
  998.   end;
  999. end;
  1000.  
  1001. procedure TDBLocate.FreeTree;
  1002. begin
  1003.   if FTree <> nil then FreeMem(FTree, FTreeSize);
  1004.   FTree := nil;
  1005.   FTreeSize := 0;
  1006. end;
  1007.  
  1008. procedure TDBLocate.CheckFilterKind;
  1009. var
  1010.   NewKind: TLocateFilter;
  1011. begin
  1012.   if CaseSensitive and LookupExact then NewKind := lfTree
  1013.   else NewKind := lfCallback;
  1014.   if (FFilterKind <> NewKind) or (NewKind = lfTree) then begin
  1015.     DropFilter;
  1016.     FFilterKind := NewKind;
  1017.   end;
  1018. end;
  1019.  
  1020. procedure TDBLocate.ActivateFilter;
  1021. begin
  1022.   CheckFilterKind;
  1023.   if FFilterHandle = nil then begin
  1024.     if FFilterKind = lfCallback then begin
  1025.       Check(DbiAddFilter(DataSet.Handle, Longint(Self), 0, True, nil,
  1026.         CallbackFilter, FFilterHandle));
  1027.     end
  1028.     else { lfTree } begin
  1029.       BuildFilterTree;
  1030.       Check(DbiAddFilter(DataSet.Handle, 0, 1, False,
  1031.         pCANExpr(FTree), nil, FFilterHandle));
  1032.     end;
  1033.   end;
  1034.   DbiActivateFilter(DataSet.Handle, FFilterHandle);
  1035. end;
  1036.  
  1037. procedure TDBLocate.DeactivateFilter;
  1038. begin
  1039.   DbiDeactivateFilter(DataSet.Handle, FFilterHandle);
  1040. end;
  1041.  
  1042. procedure TDBLocate.DropFilter;
  1043. begin
  1044.   if FFilterHandle <> nil then
  1045.     DbiDropFilter(DataSet.Handle, FFilterHandle);
  1046.   FreeTree;
  1047.   FFilterHandle := nil;
  1048. end;
  1049.  
  1050. function TDBLocate.RecordFilter(RecBuf: Pointer; RecNo: Longint): Smallint;
  1051. var
  1052.   Accept: Boolean;
  1053. begin
  1054.   try
  1055.     Move(RecBuf^, DataSet.ActiveBuffer^, DataSet.RecordSize);
  1056.     if LookupField <> nil then Accept := MatchesLookup(LookupField)
  1057.     else Accept := True;
  1058.     Result := Ord(Accept);
  1059.   except
  1060.     Application.HandleException(Self);
  1061.     Result := ABORT;
  1062.   end;
  1063. end;
  1064.  
  1065. procedure TDBLocate.ChangeBookmark;
  1066. begin
  1067.   if Bookmark <> nil then DataSet.FreeBookmark(Bookmark);
  1068.   Bookmark := DataSet.GetBookmark;
  1069. end;
  1070.  
  1071. procedure TDBLocate.ActiveChanged;
  1072. begin
  1073.   DropFilter;
  1074. end;
  1075.  
  1076. {$ENDIF WIN32}
  1077.  
  1078. { DataSet locate routines }
  1079.  
  1080. function IsFilterApplicable(DataSet: TDataSet): Boolean;
  1081. var
  1082.   Status: DBIResult;
  1083.   Filter: hDBIFilter;
  1084. begin
  1085.   if DataSet is TBDEDataSet then begin
  1086.     Status := DbiAddFilter(TBDEDataSet(DataSet).Handle, 0, 0, False, nil,
  1087.       nil, Filter);
  1088.     Result := (Status = DBIERR_NONE) or (Status = DBIERR_INVALIDFILTER);
  1089.     if Result then DbiDropFilter(TBDEDataSet(DataSet).Handle, Filter);
  1090.   end
  1091.   else Result := True;
  1092. end;
  1093.  
  1094. function DataSetFindValue(ADataSet: TBDEDataSet; const Value,
  1095.   FieldName: string): Boolean;
  1096. begin
  1097.   with TDBLocate.Create do
  1098.   try
  1099.     DataSet := ADataSet;
  1100.     if ADataSet is TDBDataSet then
  1101.       IndexSwitch := not TDBDataSet(DataSet).Database.IsSQLBased;
  1102.     Result := Locate(FieldName, Value, True, False);
  1103.   finally
  1104.     Free;
  1105.   end;
  1106. end;
  1107.  
  1108. function DataSetFindLike(ADataSet: TBDEDataSet; const Value,
  1109.   FieldName: string): Boolean;
  1110. begin
  1111.   with TDBLocate.Create do
  1112.   try
  1113.     DataSet := ADataSet;
  1114.     if ADataSet is TDBDataSet then
  1115.       IndexSwitch := not TDBDataSet(DataSet).Database.IsSQLBased;
  1116.     Result := Locate(FieldName, Value, False, False);
  1117.   finally
  1118.     Free;
  1119.   end;
  1120. end;
  1121.  
  1122. const
  1123.   SaveIndexFieldNames: TStrings = nil;
  1124.  
  1125. procedure UsesSaveIndexies;
  1126. begin
  1127.   if SaveIndexFieldNames = nil then
  1128.     SaveIndexFieldNames := TStringList.Create;
  1129. end;
  1130.  
  1131. procedure ReleaseSaveIndexies; far;
  1132. begin
  1133.   if SaveIndexFieldNames <> nil then begin
  1134.     SaveIndexFieldNames.Free;
  1135.     SaveIndexFieldNames := nil;
  1136.   end;
  1137. end;
  1138.  
  1139. procedure SetIndex(Table: TTable; const IndexFieldNames: string);
  1140. var
  1141.   IndexToSave: string;
  1142. begin
  1143.   IndexToSave := Table.IndexFieldNames;
  1144.   Table.IndexFieldNames := IndexFieldNames;
  1145.   UsesSaveIndexies;
  1146.   SaveIndexFieldNames.AddObject(IndexToSave, Table.MasterSource);
  1147. end;
  1148.  
  1149. procedure RestoreIndex(Table: TTable);
  1150. begin
  1151.   if (SaveIndexFieldNames <> nil) and (SaveIndexFieldNames.Count > 0) then
  1152.   begin
  1153.     try
  1154.       Table.IndexFieldNames :=
  1155.         SaveIndexFieldNames[SaveIndexFieldNames.Count - 1];
  1156.       Table.MasterSource :=
  1157.         TDataSource(SaveIndexFieldNames.Objects[SaveIndexFieldNames.Count - 1]);
  1158.     finally
  1159.       SaveIndexFieldNames.Delete(SaveIndexFieldNames.Count - 1);
  1160.       if SaveIndexFieldNames.Count = 0 then
  1161.         ReleaseSaveIndexies;
  1162.     end;
  1163.   end;
  1164. end;
  1165.  
  1166. procedure DeleteRange(Table: TTable; IndexFields: array of const;
  1167.   FieldValues: array of const);
  1168. var
  1169.   I: Integer;
  1170.   NewIndex: string;
  1171. begin
  1172.   NewIndex := '';
  1173.   for I := Low(IndexFields) to High(IndexFields) do begin
  1174.     NewIndex := NewIndex + TVarRec(IndexFields[I]).VString^;
  1175.     if I <> High(IndexFields) then
  1176.       NewIndex := NewIndex + ';';
  1177.   end;
  1178.   SetIndex(Table, NewIndex);
  1179.   try
  1180.     Table.SetRange(FieldValues, FieldValues);
  1181.     try
  1182.       while not Table.EOF do Table.Delete;
  1183.     finally
  1184.       Table.CancelRange;
  1185.     end;
  1186.   finally
  1187.     RestoreIndex(Table);
  1188.   end;
  1189. end;
  1190.  
  1191. procedure ReindexTable(Table: TTable);
  1192. var
  1193.   WasActive: Boolean;
  1194.   WasExclusive: Boolean;
  1195. begin
  1196.   with Table do begin
  1197.     WasActive := Active;
  1198.     WasExclusive := Exclusive;
  1199.     DisableControls;
  1200.     try
  1201.       if not (WasActive and WasExclusive) then Close;
  1202.       try
  1203.         Exclusive := True;
  1204.         Open;
  1205.         Check(dbiRegenIndexes(Handle));
  1206.       finally
  1207.         if not (WasActive and WasExclusive) then begin
  1208.           Close;
  1209.           Exclusive := WasExclusive;
  1210.           Active := WasActive;
  1211.         end;
  1212.       end;
  1213.     finally
  1214.       EnableControls;
  1215.     end;
  1216.   end;
  1217. end;
  1218.  
  1219. procedure PackTable(Table: TTable);
  1220. { This routine copied and modified from demo unit TableEnh.pas
  1221.   from Borland Int. }
  1222. var
  1223.   { FCurProp holds information about the structure of the table }
  1224.   FCurProp: CurProps;
  1225.   { Specific information about the table structure, indexes, etc. }
  1226.   TblDesc: CRTblDesc;
  1227.   { Uses as a handle to the database }
  1228.   hDb: hDbiDB;
  1229.   { Path to the currently opened table }
  1230.   TablePath: array[0..dbiMaxPathLen] of Char;
  1231.   Exclusive: Boolean;
  1232. begin
  1233.   if not Table.Active then _DBError(SDataSetClosed);
  1234.   Check(DbiGetCursorProps(Table.Handle, FCurProp));
  1235.   if StrComp(FCurProp.szTableType, szParadox) = 0 then begin
  1236.     { Call DbiDoRestructure procedure if PARADOX table }
  1237.     hDb := nil;
  1238.     { Initialize the table descriptor }
  1239.     FillChar(TblDesc, SizeOf(CRTblDesc), 0);
  1240.     with TblDesc do begin
  1241.       { Place the table name in descriptor }
  1242.       StrPCopy(szTblName, Table.TableName);
  1243.       { Place the table type in descriptor }
  1244.       StrCopy(szTblType, FCurProp.szTableType);
  1245.       bPack := True;
  1246.       bProtected := FCurProp.bProtected;
  1247.     end;
  1248.     { Get the current table's directory. This is why the table MUST be
  1249.       opened until now }
  1250.     Check(DbiGetDirectory(Table.DBHandle, False, TablePath));
  1251.     { Close the table }
  1252.     Table.Close;
  1253.     try
  1254.       { NOW: since the DbiDoRestructure call needs a valid DB handle BUT the
  1255.         table cannot be opened, call DbiOpenDatabase to get a valid handle.
  1256.         Setting TTable.Active = False does not give you a valid handle }
  1257.       Check(DbiOpenDatabase(nil, szCFGDBSTANDARD, dbiReadWrite, dbiOpenExcl, nil,
  1258.         0, nil, nil, hDb));
  1259.       { Set the table's directory to the old directory }
  1260.       Check(DbiSetDirectory(hDb, TablePath));
  1261.       { Pack the PARADOX table }
  1262.       Check(DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, False));
  1263.       { Close the temporary database handle }
  1264.       Check(DbiCloseDatabase(hDb));
  1265.     finally
  1266.       { Re-Open the table }
  1267.       Table.Open;
  1268.     end;
  1269.   end
  1270.   else if StrComp(FCurProp.szTableType, szDBase) = 0 then begin
  1271.     { Call DbiPackTable procedure if dBase table }
  1272.     Exclusive := Table.Exclusive;
  1273.     Table.Close;
  1274.     try
  1275.       Table.Exclusive := True;
  1276.       Table.Open;
  1277.       try
  1278.         Check(DbiPackTable(Table.DBHandle, Table.Handle, nil, nil, True));
  1279.       finally
  1280.         Table.Close;
  1281.       end;
  1282.     finally
  1283.       Table.Exclusive := Exclusive;
  1284.       Table.Open;
  1285.     end;
  1286.   end
  1287.   else DbiError(DBIERR_WRONGDRVTYPE);
  1288. end;
  1289.  
  1290. procedure FetchAllRecords(DataSet: TBDEDataSet);
  1291. begin
  1292.   with DataSet do
  1293.     if not EOF then begin
  1294.       CheckBrowseMode;
  1295.       Check(DbiSetToEnd(Handle));
  1296.       Check(DbiGetPriorRecord(Handle, dbiNoLock, nil, nil));
  1297.       CursorPosChanged;
  1298.       UpdateCursorPos;
  1299.     end;
  1300. end;
  1301.  
  1302. procedure BdeFlushBuffers;
  1303. var
  1304.   I, L: Integer;
  1305. {$IFDEF WIN32}
  1306.   Session: TSession;
  1307.   J: Integer;
  1308. {$ENDIF}
  1309. begin
  1310. {$IFDEF WIN32}
  1311.   for J := 0 to Sessions.Count - 1 do begin
  1312.     Session := Sessions[J];
  1313.     if not Session.Active then Continue;
  1314. {$ENDIF}
  1315.     for I := 0 to Session.DatabaseCount - 1 do begin
  1316.       with Session.Databases[I] do
  1317.         if Connected and not IsSQLBased then begin
  1318.           for L := 0 to DataSetCount - 1 do begin
  1319.             if DataSets[L].Active then
  1320.               DbiSaveChanges(DataSets[L].Handle);
  1321.           end;
  1322.         end;
  1323.     end;
  1324. {$IFDEF WIN32}
  1325.   end;
  1326. {$ENDIF}
  1327. end;
  1328.  
  1329. {$IFNDEF WIN32}
  1330. type
  1331.   TDbiGetExactRecordCount = function (hCursor: hDBICur;
  1332.     var iRecCount: Longint): DbiResult;
  1333.  
  1334. const
  1335.   DbiGetExactRecCnt: TDbiGetExactRecordCount = nil;
  1336.  
  1337. function DbiGetExactRecordCount(hCursor: hDBICur;
  1338.   var iRecCount: Longint): DbiResult;
  1339. var
  1340.   HModule: THandle;
  1341.   ErrMode: Cardinal;
  1342. begin
  1343.   if not Assigned(DbiGetExactRecCnt) then begin
  1344.     ErrMode := SetErrorMode(SEM_NOOPENFILEERRORBOX);
  1345.     HModule := LoadLibrary('IDAPI01.DLL');
  1346.     SetErrorMode(ErrMode);
  1347.     if HModule >= HINSTANCE_ERROR then begin
  1348.       @DbiGetExactRecCnt := GetProcAddress(HModule, 'DBIGETEXACTRECORDCOUNT');
  1349.       FreeLibrary(HModule);
  1350.     end;
  1351.   end;
  1352.   if Assigned(DbiGetExactRecCnt) then
  1353.     Result := DbiGetExactRecCnt(hCursor, iRecCount)
  1354.   else Result := DbiGetRecordCount(hCursor, iRecCount);
  1355. end;
  1356. {$ENDIF}
  1357.  
  1358. function DataSetRecordCount(DataSet: TDataSet): Longint;
  1359. var
  1360.   IsCount: Boolean;
  1361. begin
  1362. {$IFDEF RX_D3}
  1363.   if DataSet is TBDEDataSet then begin
  1364. {$ENDIF}
  1365.     IsCount := (DbiGetExactRecordCount(TBDEDataSet(DataSet).Handle,
  1366.       Result) = DBIERR_NONE) or (DbiGetRecordCount(TBDEDataSet(DataSet).Handle,
  1367.       Result) = DBIERR_NONE);
  1368. {$IFDEF RX_D3}
  1369.   end
  1370.   else
  1371.     try
  1372.       Result := DataSet.RecordCount;
  1373.       IsCount := True;
  1374.     except
  1375.       IsCount := False;
  1376.     end;
  1377. {$ENDIF}
  1378.   if not IsCount then Result := -1;
  1379. end;
  1380.  
  1381. function DataSetRecNo(DataSet: TDataSet): Longint;
  1382. var
  1383.   FCurProp: CURProps;
  1384.   FRecProp: RECProps;
  1385. begin
  1386.   Result := -1;
  1387.   if (DataSet <> nil) and DataSet.Active and (DataSet.State in [dsBrowse,
  1388.     dsEdit]) then
  1389.   begin
  1390. {$IFDEF RX_D3}
  1391.     if not (DataSet is TBDEDataSet) then begin
  1392.       Result := DataSet.RecNo;
  1393.       Exit;
  1394.     end;
  1395. {$ENDIF}
  1396.     if DbiGetCursorProps(TBDEDataSet(DataSet).Handle, FCurProp) <> DBIERR_NONE then
  1397.       Exit;
  1398.     if (StrComp(FCurProp.szTableType, szParadox) = 0) or
  1399.       (FCurProp.iSeqNums = 1) then
  1400.     begin
  1401.       DataSet.GetCurrentRecord(nil);
  1402.       if DbiGetSeqNo(TBDEDataSet(DataSet).Handle, Result) <> DBIERR_NONE then
  1403.         Result := -1;
  1404.     end
  1405.     else if StrComp(FCurProp.szTableType, szDBase) = 0 then begin
  1406.       DataSet.GetCurrentRecord(nil);
  1407.       if DbiGetRecord(TBDEDataSet(DataSet).Handle, dbiNOLOCK, nil, @FRecProp) = DBIERR_NONE
  1408.         then Result := FRecProp.iPhyRecNum;
  1409.     end;
  1410.   end;
  1411. end;
  1412.  
  1413. function DataSetPositionStr(DataSet: TDataSet): string;
  1414. var
  1415.   RecNo, RecCount: Longint;
  1416. begin
  1417.   try
  1418.     RecNo := DataSetRecNo(DataSet);
  1419.   except
  1420.     RecNo := -1;
  1421.   end;
  1422.   if RecNo >= 0 then begin
  1423.     RecCount := DataSetRecordCount(DataSet);
  1424.     if RecCount >= 0 then Result := Format('%d:%d', [RecNo, RecCount])
  1425.     else Result := IntToStr(RecNo);
  1426.   end
  1427.   else Result := '';
  1428. end;
  1429.  
  1430. function TransActive(Database: TDatabase): Boolean;
  1431. var
  1432.   Info: XInfo;
  1433. {$IFDEF WIN32}
  1434.   S: hDBISes;
  1435. {$ENDIF}
  1436. begin
  1437. {$IFDEF WIN32}
  1438.   Result := False;
  1439.   if DbiGetCurrSession(S) <> DBIERR_NONE then Exit;
  1440. {$ENDIF}
  1441.   Result := (Database.Handle <> nil) and
  1442.     (DbiGetTranInfo(Database.Handle, nil, @Info) = DBIERR_NONE) and
  1443.     (Info.exState = xsActive);
  1444. {$IFDEF WIN32}
  1445.   DbiSetCurrSession(S);
  1446. {$ENDIF}
  1447. end;
  1448.  
  1449. function GetBdeDirectory: string;
  1450. const
  1451.   Ident = 'DLLPATH';
  1452. var
  1453. {$IFDEF WIN32}
  1454.   Ini: TRegistry;
  1455. const
  1456.   BdeKey = 'SOFTWARE\Borland\Database Engine';
  1457. {$ELSE}
  1458.   Ini: TIniFile;
  1459. {$ENDIF}
  1460. begin
  1461.   Result := '';
  1462. {$IFDEF WIN32}
  1463.   Ini := TRegistry.Create;
  1464.   try
  1465.     Ini.RootKey := HKEY_LOCAL_MACHINE;
  1466.     if Ini.OpenKey(BdeKey, False) then
  1467.       if Ini.ValueExists(Ident) then Result := Ini.ReadString(Ident);
  1468. {$ELSE}
  1469.   Ini := TIniFile.Create('WIN.INI');
  1470.   try
  1471.     Result := Ini.ReadString('IDAPI', Ident, '');
  1472. {$ENDIF}
  1473.   { Check for multiple directories, use only the first one }
  1474.   if Pos(';', Result) > 0 then Delete(Result, Pos(';', Result), MaxInt);
  1475.   if (Length(Result) > 2) and (Result[Length(Result)] <> '\') then
  1476.     Result := Result + '\';
  1477.   finally
  1478.     Ini.Free;
  1479.   end;
  1480. end;
  1481.  
  1482. procedure ExportDataSetEx(Source: TBDEDataSet; DestTable: TTable;
  1483.   TableType: TTableType; const AsciiCharSet: string;
  1484.   AsciiDelimited: Boolean; AsciiDelimiter, AsciiSeparator: Char;
  1485.   MaxRecordCount: Longint);
  1486.  
  1487.   function ExportAsciiField(Field: TField): Boolean;
  1488.   begin
  1489.     Result := Field.Visible and not (Field.Calculated
  1490.       {$IFDEF WIN32} or Field.Lookup {$ENDIF}) and not (Field.DataType in
  1491.       ftNonTextTypes + [ftUnknown]);
  1492.   end;
  1493.  
  1494. const
  1495.   TextExt = '.TXT';
  1496.   SchemaExt = '.SCH';
  1497. var
  1498.   I: Integer;
  1499.   S, Path: string;
  1500.   BatchMove: TBatchMove;
  1501.   TablePath: array[0..dbiMaxPathLen] of Char;
  1502. begin
  1503.   if Source = nil then _DBError(SDataSetEmpty);
  1504.   if DestTable.Active then DestTable.Close;
  1505. {$IFDEF WIN32}
  1506.   if Source is TDBDataSet then
  1507.     DestTable.SessionName := TDBDataSet(Source).SessionName;
  1508. {$ENDIF}
  1509.   if (TableType = ttDefault) then begin
  1510.     if DestTable.TableType <> ttDefault then
  1511.       TableType := DestTable.TableType
  1512.     else if (CompareText(ExtractFileExt(DestTable.TableName), TextExt) = 0) then
  1513.       TableType := ttASCII;
  1514.   end;
  1515.   BatchMove := TBatchMove.Create(Application);
  1516.   try
  1517.     StartWait;
  1518.     try
  1519.       BatchMove.Mode := batCopy;
  1520.       BatchMove.Source := Source;
  1521.       BatchMove.Destination := DestTable;
  1522.       DestTable.TableType := TableType;
  1523.       BatchMove.Mappings.Clear;
  1524.       if (DestTable.TableType = ttASCII) then begin
  1525.         if CompareText(ExtractFileExt(DestTable.TableName), SchemaExt) = 0 then
  1526.           DestTable.TableName := ChangeFileExt(DestTable.TableName, TextExt);
  1527.         with Source do
  1528.           for I := 0 to FieldCount - 1 do begin
  1529.             if ExportAsciiField(Fields[I]) then
  1530.               BatchMove.Mappings.Add(Format('%s=%0:s',
  1531.                 [Fields[I].FieldName]));
  1532.           end;
  1533.         BatchMove.RecordCount := 1;
  1534.       end
  1535.       else BatchMove.RecordCount := MaxRecordCount;
  1536.       BatchMove.Execute;
  1537.       if (DestTable.TableType = ttASCII) then begin
  1538.         { ASCII table always created in "fixed" format with "ascii"
  1539.           character set }
  1540.         with BatchMove do begin
  1541.           Mode := batAppend;
  1542.           RecordCount := MaxRecordCount;
  1543.         end;
  1544.         S := ChangeFileExt(ExtractFileName(DestTable.TableName), '');
  1545.         Path := NormalDir(ExtractFilePath(DestTable.TableName));
  1546.         if Path = '' then begin
  1547.           DestTable.Open;
  1548.           try
  1549.             Check(DbiGetDirectory(DestTable.DBHandle, False, TablePath));
  1550.             Path := NormalDir(OemToAnsiStr(StrPas(TablePath)));
  1551.           finally
  1552.             DestTable.Close;
  1553.           end;
  1554.         end;
  1555.         with TIniFile.Create(ChangeFileExt(Path + S, SchemaExt)) do
  1556.         try
  1557.           if AsciiCharSet <> '' then
  1558.             WriteString(S, 'CharSet', AsciiCharSet)
  1559.           else WriteString(S, 'CharSet', 'ascii');
  1560.           if AsciiDelimited then begin { change ASCII-file format to CSV }
  1561.             WriteString(S, 'Filetype', 'VARYING');
  1562.             WriteString(S, 'Delimiter', AsciiDelimiter);
  1563.             WriteString(S, 'Separator', AsciiSeparator);
  1564.           end;
  1565.         finally
  1566.           Free;
  1567.         end;
  1568.         { clear previous output - overwrite existing file }
  1569.         S := Path + ExtractFileName(DestTable.TableName);
  1570.         if Length(ExtractFileExt(S)) < 2 then
  1571.           S := ChangeFileExt(S, TextExt);
  1572.         I := FileCreate(S);
  1573.         if I < 0 then
  1574.           raise EFCreateError.CreateFmt(ResStr(SFCreateError), [S]);
  1575.         FileClose(I);
  1576.         BatchMove.Execute;
  1577.       end;
  1578.     finally
  1579.       StopWait;
  1580.     end;
  1581.   finally
  1582.     BatchMove.Free;
  1583.   end;
  1584. end;
  1585.  
  1586. procedure ExportDataSet(Source: TBDEDataSet; DestTable: TTable;
  1587.   TableType: TTableType; const AsciiCharSet: string;
  1588.   AsciiDelimited: Boolean; MaxRecordCount: Longint);
  1589. begin
  1590.   ExportDataSetEx(Source, DestTable, TableType, AsciiCharSet,
  1591.     AsciiDelimited, '"', ',', MaxRecordCount);
  1592. end;
  1593.  
  1594. procedure ImportDataSet(Source: TBDEDataSet; DestTable: TTable;
  1595.   MaxRecordCount: Longint; Mappings: TStrings; Mode: TBatchMode);
  1596. var
  1597.   BatchMove: TBatchMove;
  1598. begin
  1599.   if Source = nil then _DBError(SDataSetEmpty);
  1600. {$IFDEF WIN32}
  1601.   if (Source is TDBDataSet) and not Source.Active then
  1602.     TDBDataSet(Source).SessionName := DestTable.SessionName;
  1603. {$ENDIF}
  1604.   BatchMove := TBatchMove.Create(Application);
  1605.   try
  1606.     StartWait;
  1607.     try
  1608.       BatchMove.Mode := Mode;
  1609.       BatchMove.Source := Source;
  1610.       BatchMove.Destination := DestTable;
  1611.       if Mappings.Count > 0 then
  1612.         BatchMove.Mappings.AddStrings(Mappings);
  1613.       BatchMove.RecordCount := MaxRecordCount;
  1614.       BatchMove.Execute;
  1615.     finally
  1616.       StopWait;
  1617.     end;
  1618.   finally
  1619.     BatchMove.Free;
  1620.   end;
  1621. end;
  1622.  
  1623. function GetNativeHandle(Database: TDatabase; Buffer: Pointer;
  1624.   BufSize: Integer): Pointer;
  1625. var
  1626.   Len: Word;
  1627. begin
  1628.   Result := nil;
  1629.   if Assigned(Database) and Database.Connected then begin
  1630.     if Database.IsSQLBased then begin
  1631.       Check(DbiGetProp(HDBIOBJ(Database.Handle), dbNATIVEHNDL,
  1632.         Buffer, BufSize, Len));
  1633.       Result := Buffer;
  1634.     end
  1635.     else DBError(SLocalDatabase);
  1636.   end
  1637.   else _DBError(SDatabaseClosed);
  1638. end;
  1639.  
  1640. procedure BdeTranslate(Locale: TLocale; Source, Dest: PChar; ToOem: Boolean);
  1641. var
  1642.   Len: Cardinal;
  1643. begin
  1644.   Len := StrLen(Source);
  1645.   if ToOem then AnsiToNativeBuf(Locale, Source, Dest, Len)
  1646.   else NativeToAnsiBuf(Locale, Source, Dest, Len);
  1647.   if Source <> Dest then Dest[Len] := #0;
  1648. end;
  1649.  
  1650. function TrimMessage(Msg: PChar): PChar;
  1651. var
  1652.   Blank: Boolean;
  1653.   Source, Dest: PChar;
  1654. begin
  1655.   Source := Msg;
  1656.   Dest := Msg;
  1657.   Blank := False;
  1658.   while Source^ <> #0 do begin
  1659.     if Source^ <= ' ' then Blank := True
  1660.     else begin
  1661.       if Blank then begin
  1662.         Dest^ := ' ';
  1663.         Inc(Dest);
  1664.         Blank := False;
  1665.       end;
  1666.       Dest^ := Source^;
  1667.       Inc(Dest);
  1668.     end;
  1669.     Inc(Source);
  1670.   end;
  1671.   if (Dest > Msg) and ((Dest - 1)^ = '.') then Dec(Dest);
  1672.   Dest^ := #0;
  1673.   Result := Msg;
  1674. end;
  1675.  
  1676. function BdeErrorMsg(ErrorCode: DBIResult): string;
  1677. var
  1678.   I: Integer;
  1679.   NativeError: Longint;
  1680.   Msg, LastMsg: DBIMSG;
  1681. begin
  1682.   I := 1;
  1683.   DbiGetErrorString(ErrorCode, Msg);
  1684.   TrimMessage(Msg);
  1685.   if Msg[0] = #0 then Result := Format(ResStr(SBDEError), [ErrorCode])
  1686.   else Result := StrPas(Msg);
  1687.   while True do begin
  1688.     StrCopy(LastMsg, Msg);
  1689.     ErrorCode := DbiGetErrorEntry(I, NativeError, Msg);
  1690.     if (ErrorCode = DBIERR_NONE) or
  1691.       (ErrorCode = DBIERR_NOTINITIALIZED) then Break;
  1692.     TrimMessage(Msg);
  1693.     if (Msg[0] <> #0) and (StrComp(Msg, LastMsg) <> 0) then
  1694.       Result := Format('%s. %s', [Result, Msg]);
  1695.     Inc(I);
  1696.   end;
  1697.   for I := 1 to Length(Result) do
  1698.     if Result[I] < ' ' then Result[I] := ' ';
  1699. end;
  1700.  
  1701. procedure DataSetShowDeleted(DataSet: TBDEDataSet; Show: Boolean);
  1702. begin
  1703.   with DataSet do begin
  1704.     CheckBrowseMode;
  1705.     Check(DbiValidateProp(hDBIObj(Handle), curSOFTDELETEON, True));
  1706.     DisableControls;
  1707.     try
  1708.       Check(DbiSetProp(hDBIObj(Handle), curSOFTDELETEON, Ord(Show)));
  1709.     finally
  1710.       EnableControls;
  1711.     end;
  1712.     if DataSet is TTable then TTable(DataSet).Refresh
  1713.     else begin
  1714.       CursorPosChanged;
  1715.       First;
  1716.     end;
  1717.   end;
  1718. end;
  1719.  
  1720. function CurrentRecordDeleted(DataSet: TBDEDataSet): Boolean;
  1721. var
  1722.   FRecProp: RECProps;
  1723. begin
  1724.   Result := False;
  1725.   if (DataSet <> nil) and DataSet.Active then begin
  1726.     DataSet.GetCurrentRecord(nil);
  1727.     if DbiGetRecord(DataSet.Handle, dbiNOLOCK, nil, @FRecProp) = DBIERR_NONE
  1728.       then Result := FRecProp.bDeleteFlag;
  1729.   end;
  1730. end;
  1731.  
  1732. procedure DbNotSupported;
  1733. begin
  1734.   DbiError(DBIERR_NOTSUPPORTED);
  1735. end;
  1736.  
  1737. procedure ToggleDebugLayer(Active: Boolean; const DebugFile: string);
  1738. const
  1739.   Options: array[Boolean] of Longint = (0, DEBUGON or OUTPUTTOFILE or
  1740.     APPENDTOLOG);
  1741. var
  1742.   FileName: DBIPATH;
  1743. begin
  1744.   Check(DbiDebugLayerOptions(Options[Active], StrPLCopy(FileName,
  1745.     DebugFile, SizeOf(DBIPATH) - 1)));
  1746. end;
  1747.  
  1748. initialization
  1749.   DbUtils.CreateLocateObject := CreateDbLocate;
  1750. {$IFDEF WIN32}
  1751. finalization
  1752.   ReleaseSaveIndexies;
  1753. {$ELSE}
  1754.   AddExitProc(ReleaseSaveIndexies);
  1755. {$ENDIF}
  1756. end.