(********************************************************************)
(*                                                                  *)
(*  archive_base.s7i  Common functions for file archives.           *)
(*  Copyright (C) 2023  Thomas Mertes                               *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library is distributed in the hope that it    *)
(*  will be useful, but WITHOUT ANY WARRANTY; without even the      *)
(*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *)
(*  PURPOSE.  See the GNU Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


(**
 *  Hash map that contains all paths in an archive as keys of the hash map.
 *  The [[ar]], [[cpio]], [[rpm]], [[tar]] and [[zip]] libraries use this type
 *  to maintain the paths of the files in the archive. Some archive libraries
 *  store additional information in the value of the hash map. This additional
 *  information is not used by [[#readDir(in_archiveRegisterType,in_string)|readDir]].
 *)
const type: archiveRegisterType is hash [string] integer;


(**
 *  Determine the file names in a directory inside an archive.
 *  The [[ar]], [[cpio]], [[rpm]], [[tar]] and [[zip]] libraries use this function.
 *  The archive libraries maintain the files in the archive with the hash map ''register''.
 *  Note that the function returns only the file names.
 *  Additional information must be obtained with other calls.
 *  @param register Hash map that contains all paths in the archive as keys of the hash map.
 *  @param dirPath Path of a directory in the archive.
 *  @return an array with the file names.
 *  @exception RANGE_ERROR ''dirPath'' does not use the standard path
 *             representation.
 *  @exception FILE_ERROR ''dirPath'' is not present in the ''register''.
 *)
const func array string: readDir (in archiveRegisterType: register, in string: dirPath) is func
  result
    var array string: fileNames is 0 times "";
  local
    var string: filePath is "";
    var boolean: dirExists is FALSE;
    var set of string: fileNameSet is (set of string).value;
    var string: fileName is "";
    var integer: slashPos is 0;
  begin
    if dirPath <> "/" and endsWith(dirPath, "/") then
      raise RANGE_ERROR;
    elsif dirPath = "" or dirPath = "." then
      for key fileName range register do
        slashPos := pos(fileName, '/');
        if slashPos = 1 then
          fileName := "/";
        elsif slashPos <> 0 then
          fileName := fileName[.. pred(slashPos)];
        end if;
        if fileName not in fileNameSet then
          incl(fileNameSet, fileName);
        end if;
      end for;
    else
      for key filePath range register do
        if startsWith(filePath, dirPath) then
          fileName := filePath[succ(length(dirPath)) ..];
          if fileName = "" then
            dirExists := TRUE;
          elsif startsWith(fileName, "/") then
            fileName := fileName[2 ..];
          elsif dirPath <> "/" then
            fileName := "";  # A file name <> dirPath starts with dirPath.
          end if;
          slashPos := pos(fileName, '/');
          if slashPos <> 0 then
            fileName := fileName[.. pred(slashPos)];
          end if;
          if fileName <> "" and fileName not in fileNameSet then
            incl(fileNameSet, fileName);
            dirExists := TRUE;
          end if;
        end if;
      end for;
      if not dirExists then
        raise FILE_ERROR;
      end if;
    end if;
    fileNames := sort(toArray(fileNameSet));
  end func;


const func boolean: implicitDir (in archiveRegisterType: register, in string: dirPath) is func
  result
    var boolean: implicitDir is FALSE;
  local
    var string: filePath is "";
  begin
    if dirPath <> "" then
      for key filePath range register do
        if startsWith(filePath, dirPath) and
            length(filePath) > length(dirPath) and
            (filePath[succ(length(dirPath))] = '/' or dirPath = "/") then
          implicitDir := TRUE;
        end if;
      end for;
    end if;
  end func;


const func boolean: isEmptyDir (in archiveRegisterType: register, in string: dirPath) is func
  result
    var boolean: isEmptyDir is TRUE;
  local
    var string: filePath is "";
  begin
    for key filePath range register do
      if startsWith(filePath, dirPath) and filePath <> dirPath then
        isEmptyDir := FALSE;
      end if;
    end for;
  end func;