IMPLEMENTATION MODULE HandleIO; (* * REPERTOIRE * Release 1.6 * By Charles Bradford and Cole Brecheen * (c) Copyright 1985-1992 PMI * Green Bay, Wisconsin * All rights reserved * (414) 468-6040 * * $Header: D:/logfiles/mods/handleio.mov 1.5 17 Mar 1991 17:54:52 coleb $ * *) (*EntryDiag: IMPORT Diagnostics; :EntryDiag*) IMPORT FAPI; IMPORT ByteFiddler; IMPORT Drectory; IMPORT EnvironUtils; IMPORT ErrorManager; IMPORT LowLevel; IMPORT M2Strings; IMPORT Numbers; IMPORT NumTypes; IMPORT StrEdit; IMPORT StringIO; IMPORT SYSTEM; IMPORT VStorage; VAR Initialized : BOOLEAN; PROCEDURE Init(); BEGIN IF Initialized THEN RETURN; ELSE Initialized := TRUE; END; (*EntryDiag: Diagnostics.Init(); :EntryDiag*) ByteFiddler.Init(); Drectory.Init(); EnvironUtils.Init(); ErrorManager.Init(); LowLevel.Init(); M2Strings.Init(); Numbers.Init(); NumTypes.Init(); StrEdit.Init(); StringIO.Init(); VStorage.Init(); (*EntryDiag: Diagnostics.diagS( 'Entering HandleIO', '' ); :EntryDiag*) DefaultFileAttribute := BITSET{}; (*Normal--not system, hidden, or ReadOnly.*) DefaultFileMode := BITSET{ 6, 1 }; (* Open for Read/Write access; allow all access requests from other processes. *) (* DefaultFileMode := BITSET{ 5, 1 }; (* Open for Read/Write access; deny write access requests by other processes, but allow read access requests. Commented out because it seems always to cause the CreateFile operation to fail with an error 32, File Sharing Violation. Doesn't interfere with OpenFile operation. *) *) (*EntryDiag: Diagnostics.diagS( 'Exiting HandleIO', '' ); :EntryDiag*) END Init; PROCEDURE OpenFile(VAR FileHandle : CARDINAL; fname : ARRAY OF CHAR) : CARDINAL; VAR ErrorReturn: CARDINAL; LongZero, Reserved: LONGINT; OpenFlag: BITSET; ActionTaken: CARDINAL; filen : ARRAY [0..79] OF CHAR; BEGIN StrEdit.AssignStr(fname, filen); FileHandle := 0; OpenFlag := {0}; (* Open if it does exist; fail if it doesn't *) LongZero := VAL(LONGINT, 0); Reserved := VAL(LONGINT, 0); ErrorReturn := FAPI.DOSOPEN( SYSTEM.ADR(filen), SYSTEM.ADR(FileHandle), SYSTEM.ADR(ActionTaken), LongZero, FAPI.FILE_NORMAL, FAPI.USHORT(OpenFlag), FAPI.USHORT(DefaultFileMode), Reserved ); RETURN ErrorReturn; END OpenFile; PROCEDURE FindFile(VAR FileHandle : CARDINAL; VAR fname: ARRAY OF CHAR; EnvironVariable : ARRAY OF CHAR) : CARDINAL; VAR SavedMessage : CARDINAL; name,PathName, ProgName, prefix, PathStr : ARRAY [0..128] OF CHAR; DriveLetter: CHAR; PROCEDURE SplitFirstDir(VAR str1, str2 : ARRAY OF CHAR); VAR lngth1, ndx : CARDINAL; BEGIN StrEdit.CutLeadingChars(' ', str1); lngth1 := M2Strings.Length(str1); ndx := M2Strings.Pos(';', str1); IF (lngth1=0) OR (ndx=0) THEN IF ndx=0 THEN M2Strings.Delete(str1, 0, 1); (*If str1 starts with a semicolon, get rid of it so it won't mess us up next time.*) END; StrEdit.SetLength(str2, 0); RETURN; END; IF ndx>HIGH(str1) THEN ndx := HIGH(str1)-1; (*We always want ndx to point to the last real character of the first directory name in str1. If there's only one directory name in str1--i.e., if there's no semicolon in str1--ndx should point to the last character of str1. If there is a semicolon in str1, ndx should point to the character immediately preceding it.*) ELSE DEC(ndx); END; M2Strings.Copy(str1, 0, ndx+1, str2); StrEdit.CutTrailingChars(' ', str2); IF (str2[M2Strings.Length(str2)-1]#'\') THEN StrEdit.Append(str2, '\'); END; M2Strings.Delete(str1, 0, ndx+1); END SplitFirstDir; PROCEDURE DirRemoved(VAR FileName : ARRAY OF CHAR) : BOOLEAN; VAR SlashSpot, ColonSpot : CARDINAL; tmp : BOOLEAN; BEGIN tmp := FALSE; SlashSpot := M2Strings.Pos('\', FileName); WHILE SlashSpot<=HIGH(FileName) DO tmp := TRUE; M2Strings.Delete(FileName, 0, SlashSpot+1); SlashSpot := M2Strings.Pos('\', FileName); END; ColonSpot := M2Strings.Pos(':', FileName); IF ColonSpot<=HIGH(FileName) THEN M2Strings.Delete(FileName, 0, ColonSpot+1); tmp := FALSE; END; RETURN (tmp); END DirRemoved; BEGIN M2Strings.Assign(fname,name); SavedMessage := OpenFile(FileHandle, fname); (*First we look for the file in any directory specified in fname. If no directory is specified in fname, we'll be looking in the current directory of the current drive at this step.*) IF SavedMessage # StringIO.NoError THEN (*First check the directory in which the program is stored.*) EnvironUtils.GetExeName(PathStr); Drectory.GetDrivePathAndName( PathStr, DriveLetter, PathName, ProgName ); M2Strings.Insert( ':', PathName, 0 ); M2Strings.Insert( DriveLetter, PathName, 0 ); IF DirRemoved(fname) THEN (*We do this to strip the old prefix out of fname.*) END; StrEdit.Append( PathName, fname ); SavedMessage := OpenFile(FileHandle, PathName); IF (SavedMessage#StringIO.NoError) THEN (* We do this to make sure we search the current directory before moving on to a path search.*) SavedMessage := OpenFile(FileHandle, fname); END; EnvironUtils.ReadEnvironment(EnvironVariable, PathStr); END; WHILE (SavedMessage#StringIO.NoError) AND (M2Strings.Length(PathStr)>0) DO SplitFirstDir(PathStr, prefix); M2Strings.Assign(name,fname); M2Strings.Insert(prefix, fname, 0); SavedMessage := OpenFile(FileHandle, fname); IF SavedMessage = StringIO.NoError THEN RETURN (SavedMessage); ELSE IF DirRemoved(fname) THEN (*We do this to strip the old prefix out of fname.*) END; END; END; RETURN (SavedMessage); END FindFile; PROCEDURE CreateFile( VAR FileHandle : CARDINAL; fname : ARRAY OF CHAR ) : CARDINAL; VAR ErrorReturn, ActionTaken: CARDINAL; OpenFlag: BITSET; Reserved: LONGINT; filen : ARRAY [0..79] OF CHAR; BEGIN StrEdit.AssignStr(fname, filen); FileHandle := 0; OpenFlag := {1, 4}; (* create if the file does not exist; truncate if it does exist *) Reserved := VAL(LONGINT, 0); ErrorReturn := FAPI.DOSOPEN( SYSTEM.ADR(filen), SYSTEM.ADR(FileHandle), SYSTEM.ADR(ActionTaken), VAL(LONGINT, 0), FAPI.FILE_NORMAL, FAPI.USHORT(OpenFlag), FAPI.USHORT(DefaultFileMode), Reserved ); RETURN ErrorReturn; END CreateFile; PROCEDURE CreateUniqueFile(VAR FileHandle : CARDINAL; VAR fname : ARRAY OF CHAR) : CARDINAL; VAR filen, TryName : ARRAY [0..79] OF CHAR; h,m,s,hs: CARDINAL; BEGIN (*RETURN ( StringIO.BadFunction); *) (* not implimented in OS/2 *) IF( fname[0]#0C) AND (fname[M2Strings.Length(fname)-1]#'\') THEN (* if not nil string make sure end with \ *) StrEdit.Append(fname,'\'); END; StrEdit.AssignStr(fname, filen); IF TRUE (*MajorVersion < 3*) THEN REPEAT EnvironUtils.GetTime( h, m, s, hs, TryName ); StrEdit.DeleteChar(':', TryName ); StrEdit.DeleteChar(' ', TryName ); StrEdit.Append( TryName, '13852940' ); StrEdit.SetLength( TryName, 12); TryName[8] := '.'; M2Strings.Insert( fname, TryName, 0 ); UNTIL NOT FileExists( TryName ); StrEdit.AssignStr( TryName, filen ); StrEdit.AssignStr( TryName, fname ); (* added 10 July 87 *) RETURN CreateFile( FileHandle, fname ); (* changed from filen 10 July 87 *) END; RETURN StringIO.NoError; END CreateUniqueFile; PROCEDURE CloseHandle(FileHandle : CARDINAL) : CARDINAL; VAR ErrorReturn: CARDINAL; BEGIN ErrorReturn := FAPI.DOSCLOSE(FileHandle); IF ErrorReturn # 0 THEN ErrorManager.WarnNumber( 'DOSCLOSE error', ErrorReturn ); END; RETURN ErrorReturn; END CloseHandle; PROCEDURE GetFilePtr(FileHandle : CARDINAL) : LONGINT; (*Returns the current DOS position in the file identified by FileHandle.*) VAR CurrentPTR: LONGINT; ErrorReturn: CARDINAL; BEGIN ErrorReturn := FAPI.DOSCHGFILEPTR( FileHandle, VAL(LONGINT, 0), 1, SYSTEM.ADR(CurrentPTR)); IF ErrorReturn # 0 THEN ErrorManager.WarnNumber("DosChgFilePtr error", ErrorReturn ); END; RETURN CurrentPTR; END GetFilePtr; PROCEDURE FileLengthWithMove(FileHandle : CARDINAL) : LONGINT; (*Returns the number of bytes in the file identified by FileHandle and leaves pointer at the file end.*) VAR CurrentPTR: LONGINT; ErrorReturn: CARDINAL; BEGIN ErrorReturn :=FAPI.DOSCHGFILEPTR( FileHandle, VAL(LONGINT, 0), 2, SYSTEM.ADR(CurrentPTR)); IF ErrorReturn # 0 THEN ErrorManager.WarnNumber("DosChgFilePtr error", ErrorReturn ); END; RETURN CurrentPTR; END FileLengthWithMove; PROCEDURE FileLength(FileHandle : CARDINAL) : LONGINT; (*Returns the number of bytes in the file identified by FileHandle.*) VAR answer, oldpos : LONGINT; BEGIN (* First, save current position so we can restore the pointer to its original location at the end.*) oldpos := GetFilePtr(FileHandle); (* now check file size with move to file end *) answer := FileLengthWithMove(FileHandle); (* now, restore the file pointer to where it was *) SetFilePtr(FileHandle, FromStart, oldpos); RETURN (answer); END FileLength; PROCEDURE EndReached(FileHandle : CARDINAL) : BOOLEAN; VAR curpos : LONGINT; ErrorReturn, BytesRead: CARDINAL; x : ARRAY [0..1] OF CHAR; BEGIN ErrorReturn := FAPI.DOSCHGFILEPTR( FileHandle, VAL(LONGINT, 0), 1, SYSTEM.ADR(curpos)); (* Get current position. *) ErrorReturn := FAPI.DOSREAD( FileHandle, SYSTEM.ADR(x), 1, SYSTEM.ADR(BytesRead)); (* Try to read one byte. *) IF BytesRead = 1 THEN (* Read was successful, so we must not have reached the end. *) ErrorReturn := FAPI.DOSCHGFILEPTR( FileHandle, curpos, 0, SYSTEM.ADR(curpos)); (* Move file pointer back to its original position. *) RETURN FALSE; ELSE RETURN TRUE; END; END EndReached; PROCEDURE FileExists(fname : ARRAY OF CHAR) : BOOLEAN; VAR FileAttribute: CARDINAL; Reserved: LONGINT; filen : ARRAY [0..79] OF CHAR; BEGIN StrEdit.AssignStr(fname, filen); Reserved := VAL(LONGINT, 0); IF FAPI.DOSQFILEMODE( SYSTEM.ADR(filen), SYSTEM.ADR(FileAttribute), Reserved)= 0 THEN RETURN TRUE; ELSE RETURN FALSE; END; END FileExists; PROCEDURE SetFilePtr(FileHandle : CARDINAL; Origin : FileOffSet; OffSet : LONGINT); VAR NewPtr : LONGINT; ErrorReturn: CARDINAL; BEGIN (* what if file length is zero??*) ErrorReturn := FAPI.DOSCHGFILEPTR(FileHandle, OffSet, ORD(Origin), SYSTEM.ADR(NewPtr)); IF ErrorReturn # 0 THEN ErrorManager.WarnNumber( "DosChgFilePtr error", ErrorReturn ); END; END SetFilePtr; PROCEDURE OpenForAppending( name : ARRAY OF CHAR; VAR handl : CARDINAL); VAR TmpName, msg: ARRAY [0..79] OF CHAR; BEGIN StrEdit.AssignStr( name, TmpName ); IF FindFile( handl, TmpName, "path") <> StringIO.NoError THEN IF CreateFile( handl, TmpName) <> StringIO.NoError THEN StrEdit.AssignStr( "Cannot create ", msg); StrEdit.Append( msg, TmpName ); StrEdit.Append( msg, '.' ); ErrorManager.WARN( msg ); END; ELSE SetFilePtr( handl, FromEnd, NumTypes.L0); END; END OpenForAppending; PROCEDURE BlockRead(FileHandle : CARDINAL; ptr : SYSTEM.ADDRESS; size : CARDINAL) : CARDINAL; VAR ErrorReturn, BytesRead: CARDINAL; BEGIN ErrorReturn := FAPI.DOSREAD( FileHandle, ptr, size, SYSTEM.ADR(BytesRead)); IF ErrorReturn # 0 THEN ErrorManager.WarnNumber( "DOSREAD error", ErrorReturn ); END; (* Looks like something may be wrong here. *) IF BytesRead < size THEN IF BytesRead = 0 THEN RETURN (StringIO.EndOfFile); ELSE RETURN (StringIO.PartialRead); END; END; RETURN ErrorReturn; END BlockRead; PROCEDURE BlockWrite(FileHandle : CARDINAL; ptr : SYSTEM.ADDRESS; size : CARDINAL) : CARDINAL; VAR ErrorReturn, BytesWritten: CARDINAL; BEGIN ErrorReturn := FAPI.DOSWRITE(FileHandle, ptr, size, SYSTEM.ADR(BytesWritten)); IF ErrorReturn # 0 THEN ErrorManager.WarnNumber( 'DOSWRITE error', ErrorReturn ); END; RETURN ErrorReturn; END BlockWrite; PROCEDURE NewHandleCopy( OldHandle: CARDINAL; VAR NewHandle: CARDINAL ): CARDINAL; (*Duplicates an open file handle and returns a new handle number that refers to the same file or device. Useful for updating files without closing them.*) VAR ErrorReturn: CARDINAL; BEGIN NewHandle := 0FFFFH; ErrorReturn := FAPI.DOSDUPHANDLE(OldHandle, SYSTEM.ADR(NewHandle)); RETURN ErrorReturn; END NewHandleCopy; PROCEDURE ForceHandleCopy( OldHandle, NewHandle: CARDINAL): CARDINAL; (*Similar to NewHandleCopy, but makes an existing handle (NewHandle) refer to another one (OldHandle). Closes NewHandle first, and then reopens it as a copy of OldHandle. Useful for dynamic I/O redirection.*) VAR ErrorReturn: CARDINAL; BEGIN ErrorReturn := FAPI.DOSDUPHANDLE(OldHandle, SYSTEM.ADR(NewHandle)); RETURN ErrorReturn; END ForceHandleCopy; PROCEDURE UpdateDisk( FileHandle: CARDINAL ); (*Writes a file's current buffers to the disk and writes its current length and time to the directory without requiring it to be closed.*) VAR ErrorCode, TmpHandle: CARDINAL; BEGIN ErrorCode:=FAPI.DOSBUFRESET( FileHandle ); IF ErrorCode # 0 THEN ErrorManager.WarnNumber('UpdateDisk Error',ErrorCode ); END; (* IF NewHandleCopy( FileHandle, TmpHandle ) = StringIO.NoError THEN StringIO.PrintMessage( CloseHandle(TmpHandle) ); END;*) END UpdateDisk; PROCEDURE FillFile( FileHandle: CARDINAL; NumberOfBytes: CARDINAL; TheByte: CHAR ): CARDINAL; VAR TmpAdr: SYSTEM.ADDRESS; SavedMessage: CARDINAL; BEGIN IF NOT VStorage.DosAvail( NumberOfBytes ) THEN RETURN StringIO.TooLittleMemory; END; VStorage.DosAlloc( TmpAdr, NumberOfBytes ); LowLevel.Fill( TmpAdr, NumberOfBytes, TheByte ); SavedMessage := BlockWrite( FileHandle, TmpAdr, NumberOfBytes ); VStorage.DosDealloc( TmpAdr, NumberOfBytes ); RETURN SavedMessage; END FillFile; BEGIN Initialized := FALSE; Init(); Init(); END HandleIO.