-- FTPProtFiles.mesa, Edit: HGM July 28, 1980  8:56 PM  

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  FTPDefs,
  FTPPrivateDefs,
  String USING [AppendDecimal, AppendLongNumber,
    AppendString, EquivalentString, StringToDecimal, StringToLongNumber];

FTPProtFiles: PROGRAM
  IMPORTS String, FTPPrivateDefs
  EXPORTS FTPPrivateDefs
  SHARES FTPDefs =
BEGIN OPEN FTPDefs, FTPPrivateDefs;
 
-- **********************!  Constants  !***********************

ftpsystem: POINTER TO FTPSystem = LocateFtpSystemObject[];


-- **********************!  Filename Primitives  !***********************

WriteFilename: PUBLIC PROCEDURE [file: STRING, propertyList: PropertyList, filePrimitives: FilePrimitives, fileSystem: FileSystem, userPropertyList: PropertyList] =
  BEGIN OPEN filePrimitives;
  -- Note:  Local filename iff filePrimitives present;
  --   accompanies remote filename with credentials.
  -- local constants
    device:    STRING = [maxStringLength];
    directory: STRING = [maxStringLength];
    name:      STRING = [maxStringLength];
    version:    STRING = [maxStringLength];
  -- local variables
    virtualFilenameObject: VirtualFilenameObject ← [
      device: device, directory: directory, name: name, version: version];
  -- local filename:  construct and output virtual filename
    IF filePrimitives # NIL THEN
      BEGIN
      DecomposeFilename[fileSystem, file, @virtualFilenameObject];
      WriteVirtualFilename[@virtualFilenameObject, propertyList, FALSE];
      END
  -- remote filename:  output virtual filename and credentials
    ELSE CopyPropertyList[userPropertyList, propertyList];
  -- output absolute filename
    WriteProperty[propertyList, serverFilename, file];
  END;

ReadFilename: PUBLIC PROCEDURE [file: STRING, propertyList: PropertyList, filePrimitives: FilePrimitives, fileSystem: FileSystem] =
  BEGIN OPEN filePrimitives;
  -- Note:  Local filename iff filePrimitives present;
  --   examines credentials accompanying local filename; takes name body property
  --   as remote filename in absence of server filename property.
  -- local constants
    device:    STRING = [maxStringLength];
    directory: STRING = [maxStringLength];
    name:      STRING = [maxStringLength];
    version:    STRING = [maxStringLength];
  -- local variables
    virtualFilenameObject: VirtualFilenameObject ← [
      device: device, directory: directory, name: name, version: version];
    property: Property;
  -- initialize filename to empty
    file.length ← 0;
  -- local filename:  use combination of absolute and virtual filenames
    IF filePrimitives # NIL THEN
      BEGIN
      -- examine credentials
        ExamineCredentials[propertyList, filePrimitives, fileSystem];
      -- initialize to absolute filename
        IF propertyList[serverFilename] # NIL THEN
          String.AppendString[file, propertyList[serverFilename]];
      -- use virtual filename to default missing filename components
        ReadVirtualFilename[@virtualFilenameObject, propertyList];
        ComposeFilename[fileSystem, file, @virtualFilenameObject];
      END
  -- remote filename:  use absolute filename
    ELSE
      BEGIN
      property ← SELECT FALSE FROM
        (propertyList[serverFilename] = NIL) => serverFilename,
        (propertyList[nameBody] = NIL) => nameBody,
        ENDCASE => version;
      IF property # version THEN String.AppendString[file, propertyList[property]];
      END;
  END;

WriteVirtualFilename: PUBLIC PROCEDURE [virtualFilename: VirtualFilename, propertyList: PropertyList, skipNullComponents: BOOLEAN] =
  BEGIN OPEN virtualFilename;
  -- Note:  Virtual filename components are never NIL.
  -- WriteComponent procedure
    WriteComponent: PROCEDURE [property: Property, value: STRING] =
      BEGIN
      IF ~(skipNullComponents AND value.length = 0) THEN
        WriteProperty[propertyList, property, value];
      END;
  -- store virtual filename components in property list
    WriteComponent[device, device];
    WriteComponent[directory, directory];
    WriteComponent[nameBody, name];
    WriteComponent[version, version];
  END;

ReadVirtualFilename: PUBLIC PROCEDURE [virtualFilename: VirtualFilename, propertyList: PropertyList] =
  BEGIN OPEN virtualFilename;
  -- Note:  Virtual filename components are never NIL.
  -- ReadComponent procedure
    ReadComponent: PROCEDURE [property: Property, value: STRING] =
      BEGIN
      IF propertyList[property] # NIL THEN
        String.AppendString[value, propertyList[property]];
      END;
  -- initialize components to empty
    device.length ← directory.length ← name.length ← version.length ← 0;
  -- retrieve virtual filename components from property list
    ReadComponent[device, device];
    ReadComponent[directory, directory];
    ReadComponent[nameBody, name];
    ReadComponent[version, version];
  END;

-- **********************!  File Attribute Primitives  !***********************

ExamineCredentials: PUBLIC PROCEDURE [propertyList: PropertyList, filePrimitives: FilePrimitives, fileSystem: FileSystem] =
  BEGIN OPEN filePrimitives;
  -- Note:  Destroys the credentials after examining them.
  -- examine primary credentials
    IF propertyList[userName] # NIL THEN
      BEGIN
      -- inspect credentials
        InspectCredentials[fileSystem, primary,
          propertyList[userName], propertyList[userPassword]];
      -- reset them to empty to avoid their being echoed
        WriteProperty[propertyList, userName, NIL]; 
        WriteProperty[propertyList, userPassword, NIL];
      END; 
  -- examine secondary credentials
    IF propertyList[connectName] # NIL THEN
      BEGIN
      -- inspect credentials
        InspectCredentials[fileSystem, secondary,
          propertyList[connectName], propertyList[connectPassword]];
      -- reset them to empty to avoid their being echoed
        WriteProperty[propertyList, connectName, NIL]; 
        WriteProperty[propertyList, connectPassword, NIL];
      END; 
  END;

WriteFileInfo: PUBLIC PROCEDURE [propertyList: PropertyList, fileInfo: FileInfo] =
  BEGIN OPEN fileInfo;
  -- Note:  EOL convention, for text files, is always CR.
  -- local constants
    textualByteSize: STRING = [maxStringLength];
    textualByteCount: STRING = [maxStringLength];
  -- encode file type
    EncodeFileType[propertyList, fileType];
  -- write byte size and byte count
    IF byteSize # 0 THEN String.AppendDecimal[textualByteSize, byteSize];
    WriteProperty[propertyList, byteSize, textualByteSize];
    IF byteCount # 0 THEN String.AppendLongNumber[textualByteCount, byteCount, 10];
    WriteProperty[propertyList, size, textualByteCount];
  -- write dates and author
    WriteProperty[propertyList, creationDate, creationDate];
    WriteProperty[propertyList, writeDate, writeDate];
    WriteProperty[propertyList, readDate, readDate];
    WriteProperty[propertyList, author, author];
  END;

ReadFileInfo: PUBLIC PROCEDURE [propertyList: PropertyList, fileInfo: FileInfo] =
  BEGIN
  -- Note:  File information object will address property list strings;
  --   EOL convention, for text files, is always CR.
  -- local constants
    textualByteSize: STRING = propertyList[byteSize];
    textualByteCount: STRING = propertyList[size];
  -- assemble file information
    fileInfo↑ ← FileInfoObject[
      fileType: DecodeFileType[propertyList],
      byteSize:
        IF textualByteSize = NIL THEN 0 ELSE String.StringToDecimal[textualByteSize],
      byteCount:
        IF textualByteCount = NIL THEN 0 ELSE String.StringToLongNumber[textualByteCount, 10],
      creationDate: propertyList[creationDate],
      writeDate: propertyList[writeDate],
      readDate: propertyList[readDate],
      author: propertyList[author]];
  END;

EncodeFileType: PUBLIC PROCEDURE [propertyList: PropertyList, fileType: FileType] =
  BEGIN
  -- local constants
    textualFileType: STRING = SELECT fileType FROM
      text       => "Text"L,
      binary     => "Binary"L,
      ENDCASE => NIL; -- unknown
  -- write file type
    WriteProperty[propertyList, type, textualFileType];
  END;

DecodeFileType: PUBLIC PROCEDURE [propertyList: PropertyList] RETURNS [fileType: FileType] =
  BEGIN
  -- local constants
    textualFileType: STRING = propertyList[type];
  -- read file type
    SELECT TRUE FROM
      (textualFileType = NIL) => fileType ← unknown;
      String.EquivalentString[textualFileType, "Text"L] => fileType ← text;
      String.EquivalentString[textualFileType, "Binary"L] => fileType ← binary;
      ENDCASE => Abort[illegalFileType];
  END;

END. -- of FTPProtFiles