-- 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