unit JConvertUtils; interface procedure Run; implementation uses Windows, Classes, SysUtils, JvSearchFiles, JvJCLUtils, JvJVCLUtils, JTools; procedure ShowHelp; begin writeln('Usage: ', ExtractFileName(ParamStr(0)), ' [-i] [-s] [-t | -b] '); writeln(#9'-i', #9, 'Convert files in-place (output overwrites input)'); writeln(#9'-s', #9, 'Recurse subdirectories'); writeln(#9'-t', #9, 'Convert to text'); writeln(#9'-b', #9, 'Convert to binary'); writeln(#9'-c', #9, 'Check without converting'); writeln(#9'-v', #9, 'Verbose output'); writeln(''); writeln(''); writeln('This program differs from Borland''s Convert utility in two ways:'); writeln(''); writeln('1. Only if a file is changed, is it written back to disk.'); writeln(#9'This is essential when working with VCS systems.'); writeln(''); writeln('2. You can use the -c option to check how many files would be converted,'); writeln(#9, 'without actually making the conversion.'); end; function InternalConvertDFM(InPlace, BinToTxt, Simulate, Verbose: boolean; const Filename: string): integer; var AFormat: TStreamOriginalFormat; InFile, OutFile: TFileStream; TmpStream: TMemoryStream; ShortFilename: string; function GetOutName(const AName: string; InPlace: boolean): string; begin if InPlace then Result := AName else Result := ChangeFileExt(AName, '.txt'); end; function IsTextResource(Stream: TStream): boolean; var Signature: integer; begin Stream.Read(Signature, sizeof(Signature)); Result := (Char(Signature) in [#9, #11, #13, ' ', 'o', 'O', 'i', 'I']); Stream.Seek(-sizeof(Signature), soFromCurrent); end; begin Result := 0; ShortFilename := ExtractRelativePath(IncludeTrailingPathDelimiter(GetCurrentDir), Filename); if (GetFileAttributes(PChar(Filename)) and FILE_ATTRIBUTE_READONLY <> 0) and not Simulate then begin writeln('WARNING: ', ShortFilename, ' is read-only (NOT converted).'); Exit; end; if Verbose then writeln('Reading ', ShortFilename); AFormat := sofUnknown; try InFile := TFileStream.Create(Filename, fmOpenRead or fmShareDenyNone); TmpStream := TMemoryStream.Create; try if BinToTxt then begin if IsTextResource(InFile) then Exit; ObjectResourceToText(InFile, TmpStream, AFormat); if AFormat = sofBinary then begin FreeAndNil(InFile); TmpStream.Seek(0, soFromBeginning); if Verbose then begin if not Simulate then writeln('Writing ', GetOutName(ShortFileName, InPlace)) else writeln('Is Binary: ', ShortFileName) end; if not Simulate then begin OutFile := TFileStream.Create(getOutName(Filename, InPlace), fmCreate); try OutFile.CopyFrom(TmpStream, 0); finally FreeAndNil(OutFile); end; end; Result := 1; end; end else begin if not IsTextResource(InFile) then Exit; ObjectTextToResource(InFile, TmpStream, AFormat); if AFormat = sofText then begin FreeAndNil(InFile); TmpStream.Seek(0, soFromBeginning); if Verbose and not Simulate then begin if not Simulate then writeln('Writing ', GetOutName(ShortFileName, InPlace)) else writeln('Is Text: ', ShortFileName) end; if not Simulate then begin OutFile := TFileStream.Create(GetOutName(Filename, InPlace), fmCreate); try OutFile.CopyFrom(TmpStream, 0); finally FreeAndNil(OutFile); end; end; Result := 1; end; end; finally InFile.Free; TmpStream.Free; end; except on E: Exception do writeln('ERROR: (', E.Message, ') in ', ShortFilename); end; end; function ConvertDFM(SubDirs, Inplace, BinToTxt, Simulate, Verbose: boolean; FileSpecs: TStringlist; var TotalCount:integer): integer; var F: TJvSearchFiles; i, j: integer; begin Result := 0; F := TJvSearchFiles.Create(nil); try if SubDirs then F.DirOption := doIncludeSubDirs else F.DirOption := doExcludeSubDirs; F.Options := F.Options - [soStripDirs]; if Verbose then writeln('Building file list, please wait...'); for i := 0 to FileSpecs.Count - 1 do begin F.RootDirectory := ExtractFilePath(FileSpecs[i]); if F.RootDirectory = '' then F.RootDirectory := GetCurrentDir else F.RootDirectory := ExpandUNCFileName(F.RootDirectory); F.DirParams.FileMask := '*.*'; F.FileParams.FileMask := ExtractFilename(FileSpecs[i]); F.FileParams.SearchTypes := [stFileMask]; if F.FileParams.FileMask = '' then F.FileParams.FileMask := '*.dfm'; F.Search; TotalCount := F.Files.Count; for j := 0 to F.Files.Count - 1 do Inc(Result, InternalConvertDFM(Inplace, BinToTxt, Simulate, Verbose, F.Files[j])); end; finally F.Free; end; end; procedure Run; var DoSubDirs, DoInplace, DoBinToTxt, DoSimulate, DoVerbose: boolean; Path: string; i,TotalCount: integer; FileSpecs: TStringlist; begin { Command switches: -s do subdirs -i write inpace -t bin -> txt -b txt -> bin -c simulate -v verbose output } // always tell them who we are: try writeln('JEDI Form Conversion Utility Version 1.0'); if (ParamCount < 1) or GetCmdSwitchValue('?', ['-', '/'], Path, true) or GetCmdSwitchValue('h', ['-', '/'], Path, true) then ShowHelp else begin FileSpecs := TStringlist.Create; try // Path is used as dummy here DoSubDirs := GetCmdSwitchValue('s', ['-', '/'], Path, true); DoInplace := GetCmdSwitchValue('i', ['-', '/'], Path, true); DoBinToTxt := GetCmdSwitchValue('t', ['-', '/'], Path, true); DoSimulate := GetCmdSwitchValue('c', ['-', '/'], Path, true); DoVerbose := GetCmdSwitchValue('v', ['-', '/'], Path, true); // Path is actually used here if not GetCmdSwitchValue('@', [], Path, true) then begin for i := 1 to ParamCount do // we must still read entire command line to find any filespecs if not (ParamStr(i)[1] in ['-', '/', '@']) then FileSpecs.Add(ParamStr(i)); end else if FileExists(Path) then // load file specs from a file FileSpecs.LoadFromFile(Path); if FileSpecs.Count = 0 then writeln('ERROR: no files specified') else begin i := ConvertDFM(DoSubDirs, DoInplace, DoBinToTxt, DoSimulate, DoVerbose, FileSpecs,TotalCount); if DoSimulate then writeln(TotalCount,' file(s) found, ',i, ' file(s) should have been converted') else writeln(TotalCount,' file(s) found, ',i, ' file(s) converted'); end; finally FileSpecs.Free; end; end; except on E: Exception do writeln('ERROR: ', E.Message); end; end; end.