!standard A.15 (00) 02-01-02 AI95-00248/04 !class amendment 00-11-28 !status work item 00-11-28 !status received 00-11-28 !priority Medium !difficulty Medium !subject Directory Operations !summary Package Ada.Directories is proposed to provide portable access to tree-structured file systems and to provide searching for files. !problem Most modern operating systems contain some sort of tree-structured file system. Many applications need to manage these file systems (by creating and removing directories, searching for files, and the like). Most Ada 95 compilers provide some sort of access to the operations needed to manage these systems. But, these packages differ in many ways, making portable Ada 95 programs impossible. The POSIX libraries provide operations for doing this, but these usually are available only on POSIX systems, leaving out many popular operating systems including MS-DOS, most flavors of Windows, and even Linux. Ada 95 has already opened the door to standard packages that are not necessarily applicable to all implementations with Ada.Command_Line. !proposal (See wording.) !wording Static Semantics with Ada.IO_Exceptions; with Ada.Calendar; package Ada.Directories is -- Directory and file operations: function Current_Directory return String; procedure Set_Directory (Directory : in String); procedure Create_Directory (New_Directory : in String; Form : in String := ""); procedure Delete_Directory (Directory : in String); procedure Create_Path (New_Directory : in String; Form : in String := ""); procedure Delete_Tree (Directory : in String); procedure Delete_File (Name : in String); procedure Rename (Old_Name, New_Name : in String); procedure Copy_File (Source_Name, Target_Name : in String); -- File and directory name operations: function Full_Name (Name : in String) return String; function Simple_Name (Name : in String) return String; function Containing_Directory (Directory : in String) return String; function Extension_Name (Name : in String) return String; function Base_Name (Name : in String) return String; function Compose (Containing_Directory, Name, Extension : in String := "") return String; -- File and directory queries: type File_Kind is (Directory, Ordinary_File, Special_File); type File_Size is range 0 .. ; function Kind (Name : in String) return File_Kind; function Size (Name : in String) return File_Size; function Modification_Time (Name : in String) return Ada.Calendar.Time; -- Directory searching: type Directory_Entry_Type is limited private; type Filter_Type is array (File_Kind) of Boolean; type Search_Type is limited private; function Is_Valid (Search : in Search_Type) return Boolean; procedure Start_Search (Search : in out Search_Type; Directory : in String; Pattern : in String; Filter : in Filter_Type := (others => True)); procedure End_Search (Search : in out Search_Type); procedure Get_Next_Match (Search : in out Search_Type; Directory_Entry : out Directory_Entry_Type); -- Operations on Directory Entries: function Is_Valid (Directory_Entry : in Directory_Entry_Type) return Boolean; function Simple_Name (Directory_Entry : in Directory_Entry_Type) return String; function Full_Name (Directory_Entry : in Directory_Entry_Type) return String; function Kind (Directory_Entry : in Directory_Entry_Type) return File_Kind; function Size (Directory_Entry : in Directory_Entry_Type) return File_Size; function Modification_Time (Directory_Entry : in Directory_Entry_Type) return Ada.Calendar.Time; Status_Error : exception renames Ada.IO_Exceptions.Status_Error; Name_Error : exception renames Ada.IO_Exceptions.Name_Error; Use_Error : exception renames Ada.IO_Exceptions.Use_Error; Device_Error : exception renames Ada.IO_Exceptions.Device_Error; private -- Not specified by the language. end Ada.Directories; External files may be classified as directories, special files, or ordinary files. A @i is an external file that is a container for files on the target system. A @i is an external file that cannot be created or read by a predefined Ada Input-Output package. External files that are not special files or directories are called @i. AARM Ramification: A directory is an external file, although it may not have a name on some targets. A directory is not a special file, as it can be created and read by Ada.Directories. AARM Discussion: Devices and soft links are examples of special files on Windows and Unix. Even if an implementation provides a package to create and read on soft links, such links are still special files. A @i is a string identifying an external file. Similarly, a @i is a string identifying a directory. The interpretation of file names and directory names is implementation-defined. The @i of an external file is a full specification of the name of the file. If the external environment allows alternative specifications of the name (for example, abbreviations), the full name should not use such alternatives. A full name typically will include the names of all of directories that contain the item. The @i of an external file is the name of the item, not including any containing directory names. Unless otherwise specified, a file name or directory name parameter to a predefined Ada input-output subprogram can be a full name, a simple name, or any other form of name supported by the implementation. AARM Discussion: The full name on Unix is a complete path to the root. For Windows, the full name includes a complete path, as well as a disk name ("C:") or network share name. For both systems, the simple name is the part of the name following the last '/' (or '\' for Windows). For example, in the name "/usr/randy/ada-directories.ads", "ada-directories.ads" is the simple name. AARM Ramification: It is possible for a file or directory name to be neither a full name nor a simple name. For instance, the Unix name "../parent/myfile" is neither a full name nor a simple name. The @i is the directory that is used if a directory or file name is not a full name (that is, when the name does not fully identify the containing directories). AARM Discussion: The default directory is the one maintained by the familar "cd" command on Unix and Windows. Note that Windows maintains separate default directories for each disk drive; implementations should use the natural implementation. A @i is a single item in a directory, identifying a single external file (including directories and special files). For each function that returns a string, the lower bound of the returned value is 1. function Current_Directory return String; Returns the full directory name for the current default directory. The name returned shall be suitable for a future call to Set_Current_Directory. The exception Use_Error is propagated if a default directory is not supported by the external environment. procedure Set_Directory (Directory : in String); Sets the current default directory. The exception Name_Error is propagated if the string given as Directory does not identify an existing directory. The exception Use_Error is propagated if the external environment does not support making Directory (in the absence of Name_Error) a default directory. procedure Create_Directory (New_Directory : in String; Form : in String := ""); Create a directory with name New_Directory. The Form can be used to give system-dependent characteristics of the directory; the interpretation of the Form parameter is implementation-defined. A null string for Form specifies the use of the default options of the implementation of the new directory. The exception Name_Error is propagated if the string given as New_Directory does not allow the identification of a directory. The exception Use_Error is propagated if the external environment does not support the creation of a directory with the given name (in the absence of Name_Error) and form. procedure Delete_Directory (Directory : in String); Delete an existing empty directory with name Directory. The exception Name_Error is propagated if the string given as Directory does not identify an existing directory. The exception Use_Error is propagated if the external environment does not support the deletion of the directory (or some portion of its contents) with the given name (in the absence of Name_Error). procedure Create_Path (New_Directory : in String; Form : in String := ""); Create zero or more directories with name New_Directory. Each non-existent directory named by New_Directory is created. For example, on a typical Unix system, Create_Tree ("/usr/me/my"); would create directory "me" in directory "usr", then create directory "my" in directory "me". The Form can be used to give system-dependent characteristics of the directory; the interpretation of the Form parameter is implementation-defined. A null string for Form specifies the use of the default options of the implementation of the new directory. The exception Name_Error is propagated if the string given as New_Directory does not allow the identification of any directory. The exception Use_Error is propagated if the external environment does not support the creation of any directories with the given name (in the absence of Name_Error) and form. procedure Delete_Tree (Directory : in String); Delete an existing directory with name Directory. The directory and all of its contents (possibly including other directories) are deleted. The exception Name_Error is propagated if the string given as Directory does not identify an existing directory. The exception Use_Error is propagated if the external environment does not support the deletion of the directory or some portion of its contents with the given name (in the absence of Name_Error). If Use_Error is propagated, it is unspecified if a portion of the contents of the directory are deleted. procedure Delete_File (Name : in String); Delete an existing ordinary or special file with Name. The exception Name_Error is propagated if the string given as Name does not identify an existing ordinary or special external file. The exception Use_Error is propagated if the external environment does not support the deletion of the file with the given name (in the absence of Name_Error). procedure Rename (Old_Name, New_Name : in String); Rename an existing external file (including directories) with Old_Name to New_Name. The exception Name_Error is propagated if the string given as Old_Name does not identify an existing external file. The exception Use_Error is propagated if the external environment does not support the renaming of the file with the given name (in the absence of Name_Error). In particular, Use_Error is propagated if a file or directory already exists with New_Name. procedure Copy_File (Source_Name, Target_Name : in String); Copy the contents of the existing external file with Source_Name to Target_Name. The resulting external file is a duplicate of the source external file. Exception Name_Error is propagated if the string given as Source_Name does not identify an existing external ordinary or special file or if the string given as Target_Name does not allow the identification of an external file. The exception Use_Error is propagated if the external environment does not support the creating of the file with the name given by Target_Name or copying of the file with the name given by Source_Name (in the absence of Name_Error). AARM Ramification: Name_Error is always raised if Source_Name identifies a directory. It is up to the implementation whether special files can be copied, or if Use_Error will be raised. function Full_Name (Name : in String) return String; Returns the full name corresponding to the file name specified by Name. The exception Name_Error is propagated if the string given as Name does not allow the identication of an external file (including directories and special files). AARM Discussion: Full name means that no abbrevations are used in the returned name, and that it is a full specification of the name. Thus, for Unix and Windows, the result should be a full path which does not contain any "." or ".." directories. Typically, the default directory is used to fill in any missing information. function Simple_Name (Name : in String) return String; Returns the simple name portion of the file name specified by Name. The exception Name_Error is propagated if the string given as Name does not allow the identication of an external file (including directories and special files). function Containing_Directory (Name : in String) return String; Returns the full name of the containing directory of the external file (including directories) identified by Name. (If more than one directory can contain Name, the directory name returned is implementation-defined.) The exception Name_Error is propagated if the string given as Name does not does not allow the identification of an external file. The exception Use_Error is propagated if the external file does not have a containing directory. AARM Discussion: If Name is not given as a full name, the default directory is used to determine the name in the manner appropriate for the external environment. For example, if the current directory on Windows is "C:\Ada95\RM" and Containing_Directory ("..\AARM\RM-A-8") is called, the result should be "C:\Ada95\AARM". function Extension_Name (Name : in String) return String; Returns the extension name corresponding to Name. The extension name is a portion of a simple name (not including any separator characters), typically used to identify the file class. If the external environment does not have extension names, then the null string is returned. The exception Name_Error is propagated if the string given as Name does not does not allow the identification of an external file. AARM Discussion: For Unix and Windows, the extension is the portion of the simple name following the rightmost period. For example, in the simple name "RM-A-8.html", the extension is "html". function Base_Name (Name : in String) return String; Returns the base name corresponding to Name. The base name is the remainder of a simple name after removing any extension and extension separators. The exception Name_Error is propagated if the string given as Name does not allow the identication of an external file (including directories and special files). AARM Discussion: For Unix and Windows, the base name is the portion of the simple name preceding the rightmost period. For example, in the simple name "RM-A-8.html", the base name is "RM-A-8". function Compose (Containing_Directory, Name, Extension : in String := "") return String; Returns the full name of the external file with the specified Containing_Directory, Name, and Extension. If Extension is the null string, then Name is interpreted as a simple name; otherwise Name is interpreted as a base name. If Containing_Directory is the null string, then the default containing directory is used. The exception Name_Error is propagated if the string given as Containing_Directory is not null and does not allow the identication of a directory, or if the string given as Extension is not null and is not a possible extension, or if the string given as Name is not null and is not a possible simple name (if Extension is null) or base name (if Extension is non-null). type File_Kind is (Directory, Ordinary_File, Special_File); The type File_Kind represents the kind of file represented by by an external file or directory. type File_Size is range 0 .. ; The type File_Size represents the size of an external file. function Kind (Name : in String) return File_Kind; Returns the kind of external file represented by Name. The exception Name_Error is propagated if the string given as Name does not allow the identication of an existing external file. function Size (Name : in String) return File_Size; Returns the size of the external file represented by Name. The size of an external file is the number of stream elements that contained in the file. If the external file is discontiguous (not all elements exist), the result is implementation-defined. If the external file is not an ordinary file, the result is implementation-defined. The exception Name_Error is propagated if the string given as Name does not allow the identication of an existing external file. The exception Constraint_Error is propagated if the file size is not a value of type File_Size. AARM Discussion: We allow raising Constraint_Error, so that an implementation for a system with 64-bit file sizes does not need to support full numerics on 64-bit integers just to implement this package. Of course, if 64-bit integers are available on such a system, they should be used when defining type File_Size. function Modification_Time (Name : in String) return Ada.Calendar.Time; Returns the time that the external file represented by Name was most recently modified. If the external file is not an ordinary file, the result is implementation-defined. The exception Name_Error is propagated if the string given as Name does not allow the identication of an existing external file. The exception Use_Error is propagated if the external environment does not support the reading the modification time of the file with the name given by Name (in the absence of Name_Error). type Directory_Entry_Type is limited private; The type Directory_Entry_Type represents a single item in a directory. These items can only be created by the Get_Next_Match procedure in this package. Information about the item can be obtained from the functions declared in this package. A default initialized object of this type shall be invalid. type Filter_Type is array (File_Kind) of Boolean; The type Filter_Type specifies which directory entries are provided from a search operation. If the Directory component is True, directory entries representing directories are provided. If the Ordinary_File component is True, directory entries representing ordinary files are provided. If the Special_File component is True, directory entries representing special files are provided. type Search_Type is limited private; The type Search_Type contains the state of a directory search. A default-initialized Search_Type object is invalid. function Is_Valid (Search : in Search_Type) return Boolean; Returns True if Search is a valid search, and False otherwise. procedure Start_Search (Search : in out Search_Type; Directory : in String; Pattern : in String; Filter : in Filter_Type := (others => True)); Starts a search in the directory entry in the directory named by Directory for entries matching Pattern. Pattern represents a file name matching pattern; its interpretation is implementation-defined. Only items which match Filter will be returned. After a sucessful call on Start_Search, the object Search will be a valid search. The exception Name_Error is propagated if the string given by Directory does not identify an existing directory, or if Pattern does not allow the identification of any possible external file or directory. The exception Use_Error is propagated if the external environment does not support the searching of the directory with the given name (in the absence of Name_Error). AARM Discussion: Pattern should use the pattern matching characters commonly used on the target. For instance, on Unix and Windows, both '*' and '?' should be supported, with their conventional meaning. procedure End_Search (Search : in out Search_Type); Ends the search represented by Search. After a successful call on End_Search, the object Search will be invalid. The exception Status_Error is propagated if Search is not valid. procedure Get_Next_Match (Search : in out Search_Type; Directory_Entry : out Directory_Entry_Type); Returns the next Directory_Entry for the search described by Search that matches the pattern and filter. If no further matches are available, Directory_Entry is invalid after this call. It is implementation-defined as to whether the results returned by this routine are altered if the contents of the directory are altered while the Search object is valid (for example, by another program). The exception Use_Error is propagated if the external environment does not support continued searching of the directory represented by Search. function Is_Valid (Directory_Entry: in Directory_Entry_Type) return Boolean; Returns True if Directory_Entry is valid, and False otherwise. function Simple_Name (Directory_Entry : in Directory_Entry_Type) return String; Returns the simple external name of the external file (including directories) represented by Directory_Entry. The format of the name returned is implementation-defined. The exception Status_Error is propagated if Directory_Entry is invalid. function Full_Name (Directory_Entry : in Directory_Entry_Type) return String; Returns the full external name of the external file (including directories) represented by Directory_Entry. The format of the name returned is implementation-defined. The exception Status_Error is propagated if Directory_Entry is invalid. function Kind (Directory_Entry : in Directory_Entry_Type) return File_Kind; Returns the kind of external file represented by Directory_Entry. The exception Status_Error is propagated if Directory_Entry is invalid. function Size (Directory_Entry : in Directory_Entry_Type) return File_Size; Returns the size of the external file represented by Directory_Entry. The size of an external file is the number of stream elements that contained in the file. If the external file is discontiguous (not all elements exist), the result is implementation-defined. If the external file represented by Directory_Entry is not an ordinary file, the result is implementation-defined. The exception Status_Error is propagated if Directory_Entry is invalid. The exception Constraint_Error is propagated if the file size is not a value of type File_Size. function Modification_Time (Name : in String) return Ada.Calendar.Time; Returns the time that the external file represented by Directory_Entry was most recently modified. If the external file represented by Directory_Entry is not an ordinary file, the result is implementation-defined. The exception Status_Error is propagated if Directory_Entry is invalid. The exception Use_Error is propagated if the external environment does not support the reading the modification time of the file with the name given by Name (in the absence of Name_Error). Implementation Requirements For Copy_File, if Source_Name identifies an existing external ordinary file created by a predefined Ada Input-Output package, and Target_Name can be used in the Create operation of that Input-Output package with mode Out_File without raising an exception, then then Copy_File shall not propagate Use_Error. AARM Discussion: This means that Copy_File will copy any file that the Ada programmer could copy (by writing some possibly complicated Ada code). Implementation Advice If other information about a file is available (such as the size or creation date) in a directory entry, the implementation should provide functions in a child package Ada.Directories.Information to retrieve it. Start_Search should raise Use_Error if Pattern is malformed, but not if it could represent a file in the directory but does not actually do so. For Rename, if both New_Name and Old_Name are simple names, then Rename should not propagate Use_Error. AARM Discussion: This cannot be a requirement, since we we have to allow Use_Error to be raised (for instance, because the user may not have permission to rename the file, even if New_Name doesn't exist and is a valid name). Notes The file name operations Containing_Directory, Full_Name, Simple_Name, Base_Name, Extension, and Compose operate on file names, not external files. The files identified by these operations do not need to exist. Name_Error is raised only if the file name is malformed and cannot possibly identify a file. Values of Search_Type and Directory_Entry_Type can be saved and queried later. However, another task or application can modify or delete the file represented by a Directory_Entry_Type value or the directory represented by a Search_Type value; such a value can only give the information valid at the time it is created. Therefore, long-term storage of these values is not recommended. If the target system does not support directories inside of directories, Is_Directory will always return False, and Containing_Directory will always raise Use_Error. If the target system does not support creation or deletion of directories, Create_Directory, Create_Path, Delete_Directory, and Delete_Tree will always propagate Use_Error. The second sentence of A.8.2(22) is deleted. (The Full_Name function provides the needed functionality without any overhead on Open and Create.) !example Searching a directory for all files with a particular file type: with Ada.Text_IO, Ada.Directories; procedure Find_Executables is Search : Ada.Directories.Search_Type; Item : Ada.Directories.Directory_Entry_Type; use type Ada.Directories.Directory_Entry_Type; begin Ada.Directories.Start_Search (Search => Search, Directory => "C:\Bin", Pattern => "*.EXE", Filter => Ada.Directories.Only_Files); loop Ada.Directories.Get_Next_Match (Search => Search, Directory_Entry => Item); exit when not Ada.Directories.Is_Valid (Item); -- Already have all matches. Ada.Text_IO.Put_Line ("Found file: " & Ada.Directories.Simple_Name(Item)); -- Do something with the file. end loop; Ada.Directories.End_Search (Search => Search); end Find_Executables; !discussion The proposed package is based on the existing Claw package (Claw.Directories) [which was designed for Microsoft Windows only], the Ada POSIX bindings, and the GNAT package GNAT.Directory_Operations. The Ada POSIX bindings cannot be used directly, because they are rather UNIX-centric. For instance, Create_Directory takes a POSIX_Permissions parameter, which is very difficult to map to another operating system. They also define an entire new set of exceptions, which would complicate the definition of the feature. The names of the routines are based on those used in POSIX and GNAT. Claw uses shorter names based on the idea that the parameter and/or prefix will make it clear what type of object is being created. For example, Create_Directory is Create, and Current_Directory is Current. This seems too radical. We considered unifying the Delete_File and Delete_Directory operations as Delete, which would slightly simplify the description of the operation. However, since both Unix and Windows use different system calls to implement Delete_File and Delete_Directory, the implementation would be substantially more complex (requiring determining the file kind before proceeding). That seemed to complex, especially as the programmer typically knows whether they are deleting a file or directory. POSIX and Claw provide an iterator verion of the directory searching mechanism. An earlier version of the proposal used the iterator version in preference to the current object-based version. The iterator version was dropped because a significant number of reviewers had problems understanding how it works. The object-based version is considerably easier to understand. POSIX and GNAT provide only the item name as a result from a directory search operation. This package provides an private type, in order that other information can be provided, such as the file size and modification time. Moreover, we encourage providing other information if it is available. (For instance, Windows provides creation and last access time stamps in a directory entry.) Of course, using such information is not portable. The implementation advice above shows the intent. The package is defined as a child of Ada. This is necessary as the package is designed to work with all kinds of files, so it would be inappropriate for it to be a child of any specific IO package, and making it a child of IO_Exceptions also seems inappropriate. Moreover, we could cause conflicts by defining grandchildren of Ada, but not for children of Ada (since it is illegal to compile children of Ada, and implementors should not define their own, while there are no such restrictions for grandchildren of Ada). A.8.2(22) has long been tested by the ACATS as a requirement. A proper implementation of this rule is expensive, as every open must save the full name in order to be tolerant of changes in the default directory. (An Ada 95 implementation could be unfriendly and ignore changes in the default directory, but the advent of this package would make that implementation incorrect). It is better to provide those operations to those who need them; so Full_Name is included with this package (with a requirement to support the old A.8.2(22)), and the rule has been deleted from A.8.2(22). Thus, the effect of the Ada 95 Name can be provided by: Ada.Directories.Full_Name(Name(File)) directly after opening a file without the overhead for users that don't need it. !ACATS Test ACATS test(s) need to be created. !appendix From: Randy Brukardt Sent: Wednesday, October 04, 2000 9:35 PM Most modern operating systems contain some sort of tree-structured file system. Many applications need to manage these file systems (by creating and removing directories, searching for files, and the like). Most Ada 95 compilers provide some sort of access to the operations needed to manage these systems. But, these packages differ in many ways, making portable Ada 95 programs impossible. The POSIX libraries provide operations for doing this, but these usually are available only on POSIX systems, leaving out many popular operating systems including MS-DOS, most flavors of Windows, and even Linux. Ada 95 has already opened the door to standard packages that are not necessarily applicable to all implementations with Ada.Command_Line. Therefore, I am proposing a package Ada.Directories. This package is based on the existing Claw package (Claw.Directories) [which was designed for Microsoft Windows only], the Ada POSIX bindings, and the GNAT package GNAT.Directory_Operations. Static Semantics with Ada.IO_Exceptions; package Ada.Directories is function Get_Current_Directory return String; procedure Set_Current_Directory (Directory : in String); procedure Create_Directory (New_Directory : in String; Form : in String := ""); procedure Remove_Directory (Directory : in String); type Directory_Entry_Type is private; No_Directory_Entry : constant Directory_Entry_Type; type Filter_Type is (Everything, Only_Directories, Only_Files); generic with procedure Action (Directory_Entry: in Directory_Entry_Type; Quit : in out Boolean); procedure For_Every_Directory_Entry (Directory : in String); generic with procedure Action (Directory_Entry: in Directory_Entry_Type; Quit : in out Boolean); procedure For_Matching_Directory_Entries (Directory : in String; Pattern : in String; Returns : in Filter_Type := Claw.Directories.Everything); -- Operations on Directory Entries: function Is_Valid (Directory_Entry: in Directory_Entry_Type) return Boolean; function Name_of (Directory_Entry : in Directory_Entry_Type) return String; function Is_Directory (Directory_Entry : in Directory_Entry_Type) return Boolean; function Is_Ordinary_File (Directory_Entry : in Directory_Entry_Type) return Boolean; Name_Error : exception renames Ada.IO_Exceptions.Name_Error; Use_Error : exception renames Ada.IO_Exceptions.Use_Error; Device_Error : exception renames Ada.IO_Exceptions.Device_Error; private -- Not specified by the language. end Ada.Directories; A @i is a container for files on the target system. A @ is a string identifying a directory. The interpretation of directory name strings is implementation-defined. A @i is a single item in a directory, identifying a single external file or directory. function Get_Current_Directory return String; Returns the directory name string for the current default directory. The name returned shall be suitable for a future call to Set_Current_Directory. The exception Use_Error is propagated if the external environment does not support a default directory. procedure Set_Current_Directory (Directory : in String); Sets the current default directory. The exception Name_Error is propagated if string Directory does not identify an existing directory. The exception Use_Error is propagated if the external environment does not support making Directory (in the absence of Name_Error) a default directory. procedure Create_Directory (New_Directory : in String; Form : in String := ""); Create a directory with name New_Directory. The Form can be used to give system-dependent characteristics of the directory; the interpretation of the Form parameter is implementation-defined. A null string for Form specifies the use of the default options of the implementation of the new directory. The exception Name_Error is propagated if string New_Directory does not identify a possible directory. The exception Use_Error is propagated if the external environment does not support the creation of a directory with the given name (in the absence of Name_Error) and Form. procedure Remove_Directory (Directory : in String); Remove an existing directory with name Directory. The exception Name_Error is propagated if string Directory does not identify an existing directory. The exception Use_Error is propagated if the external environment does not support the deletion of the directory with the given name (in the absence of Name_Error). type Directory_Entry_Type is private; The type Directory_Entry_Type represents a single item in a directory. These items can only be created by the generic directory searching procedures in this package. Information about the item can be obtained from the functions declared in this package. No_Directory_Entry represents an invalid directory entry. A default initialized object of this type shall be set to No_Directory_Entry. type Filter_Type is (Everything, Only_Directories, Only_Files); The type Filter_Type specifies which directory entries are provided from a search operation. Everything specifies that all matching directory entries are provided. Only_Directories specifies that only directory entries representing directories are provided. Only_Files specifies that only directory entries representing ordinary files (not directories) are provided. generic with procedure Action (Directory_Entry: in Directory_Entry_Type; Quit : in out Boolean); procedure For_Every_Directory_Entry (Directory : in String); Calls Action once for each directory entry in the directory named by Directory. When Action is called, Directory_Entry contains a valid directory entry, and Quit is False. If Action sets Quit to True, For_Every_Directory_Entry finishes with no further calls to action. The exception Name_Error is propagated if string Directory does not identify an existing directory. The exception Use_Error is propagated if the external environment does not support the searching of the directory with the given name (in the absence of Name_Error). generic with procedure Action (Directory_Entry: in Directory_Entry_Type; Quit : in out Boolean); procedure For_Matching_Directory_Entries (Directory : in String; Pattern : in String; Returns : in Filter_Type := Claw.Directories.Everything); Calls Action once for each directory entry in the directory named by Directory which matches Returns and Pattern. Pattern represents a file name matching pattern; its interpretation is implementation-defined. When Action is called, Directory_Entry contains a valid directory entry, and Quit is False. If Action sets Quit to True, For_Every_Directory_Entry finishes with no further calls to action. The exception Name_Error is propagated if string Directory does not identify an existing directory, or if Pattern does not identify any possible file. The exception Use_Error is propagated if the external environment does not support the searching of the directory with the given name (in the absence of Name_Error). function Is_Valid (Directory_Entry: in Directory_Entry_Type) return Boolean; Returns True if Directory_Entry is valid, and False otherwise. function Name (Directory_Entry : in Directory_Entry_Type) return String; Returns the external name of the file, directory, or other item represented by Directory_Entry. The format of the name returned is implementation-defined. The exception Use_Error is propagated if Directory_Entry is invalid. function Is_Directory (Directory_Entry : in Directory_Entry_Type) return Boolean; Returns True if the item represented by Directory_Entry is a directory, and False otherwise. The exception Use_Error is propagated if Directory_Entry is invalid. function Is_Ordinary_File (Directory_Entry : in Directory_Entry_Type) return Boolean; Returns True if the item represented by Directory_Entry is an ordinary file (not a directory), and False otherwise. The exception Use_Error is propagated if Directory_Entry is invalid. Implementation Advice If other information about a file is available (such as the size or creation date) in a directory entry, the implementation should provide functions in a child package Ada.Directories.Information to retrieve it. The name returned by Name should be a file name without any directory information. Notes Values of Directory_Entry_Type can be copied and queried later. However, another task or application can modify or delete the file represented by a Directory_Entry_Type value; such a value can only give the information valid at the time it is created. Therefore, long-term storage of these values is not recommended. If the target system does not support directories inside of directories, Is_Directory will always return False. If the target system does not support creation or deletion of directories, Create_Directory and Remove_Directory will always propagate Use_Error. Action may not be called at all for For_Every_Directory_Entry and For_Matching_Directory_Entries if there are no matching directory entries. Design Notes: 1) The names of the routines are based on those used in POSIX and GNAT. Claw uses shorter names based on the idea that the parameter and/or prefix will make it clear what type of object is being created. For example, Create_Directory is Create, and Get_Current_Directory is Get_Current. This seems too radical. 2) GNAT and Claw provide an "open" (non-iterator) version of the directory searching mechanism. I've not included that, because it may not be easy to provide an operation which can be saved/continued at a (much) later time (even in the same application.) The iterator eliminates the need to store the state of searching outside of the package. 3) POSIX and GNAT provide only the item name as a result from a directory search operation. This package provides an private type, in order that other information can be provided as it is available. (For instance, Windows provides various time stamps in a directory entry.) Of course, using such information is not portable. The implementation advice above shows the intent. 4) The intent is that Pattern in For_Matching_Directory_Entries matches the standard on the target system. For instance, on Windows, * and ? are the wildcard characters. I don't know of a good way to say this. Comments, brickbats welcome. **************************************************************** From: Robert Dewar Sent: Wednesday, October 04, 2000 9:55 PM I am dubious about trying to standardize functionality of this kind at this stage. If we do decide that this is a worth while approach then we should take a comprehensive view of what is needed, rather than carve out individual pieces haphazardly. Incidentally, in GNAT, we only make things part of the Ada hierarchy if we think they are possible candidates for such treatment. The current set of such files in Ada is: Ada.Command_Line.Remove Ada.Direct_IO.C_Streams Ada.Exceptions.Is_Null_Occurrence Ada.Sequential_IO.C_Streams Ada.Streams.Stream_IO.C_Streams Ada.Strings.Unbounded.Text_IO Ada.Strings.Wide_Unbounded.Wide_Text_IO Ada.Task_Identification.Image Ada.Text_IO.C_Streams Ada.Wide_Text_IO.C_Streams The packages we add to System are: System.Address_Image System.Assertions System.Partition_Interface System.Task_Info System.Wch_Cnv System.Wch_Con It is indeed true that our package GNAT.Directory_Operations is NOT in the Ada or System hierarchies, which means that we do not consider this suitable area for standardization. However, if this *is* an area for standardization, I think that the minimal richness is what is found in the GNAT unit, and I find the proposed Ada child too bare. Here is the GNAT spec: -- Directory operations -- This package provides routines for manipulating directories. A directory -- can be treated as a file, using open and close routines, and a scanning -- routine is provided for iterating through the entries in a directory. package GNAT.Directory_Operations is subtype Dir_Name_Str is String; -- A subtype used in this package to represent string values that are -- directory names. A directory name is a prefix for files that appear -- with in the directory. This means that for Unix systems, the string -- includes a final '/', and for DOS-like systems, it includes a final -- '\' character. It can also include drive letters if the operating -- system provides for this. The final '/' or '\' in a Dir_Name_Str is -- optional when passed as a procedure or function in parameter. type Dir_Type is limited private; -- A value used to reference a directory. Conceptually this value includes -- the identity of the directory, and a sequential position within it. Null_Dir : constant Dir_Type; -- Represent the value for an uninitialized or closed directory. Directory_Error : exception; -- Exception raised if the directory cannot be opened, read, closed, -- created or if it is not possible to change the current execution -- environment directory. procedure Change_Dir (Dir_Name : Dir_Name_Str); -- Changes the working directory of the current execution environment -- to the directory named by Dir_Name. -- -- Raises Directory_Error if Dir_Name does not exist. procedure Make_Dir (Dir_Name : Dir_Name_Str); -- Create a new directory named Dir_Name. -- -- Raises Directory_Error if Dir_Name cannot be created. function Get_Current_Dir return Dir_Name_Str; -- Returns the current working directory for the execution environment. procedure Get_Current_Dir (Dir : out Dir_Name_Str; Last : out Natural); -- Returns the current working directory for the execution -- environment. The name is returned in Dir_Name; Last is the index in -- Dir_Name such that Dir_Name (Last) is the last character written. If -- Dir_Name is too small for the directory name, the name will be -- truncated before beeing copied to Dir_Name. procedure Open (Dir : out Dir_Type; Dir_Name : in Dir_Name_Str); -- Opens the directory named by Dir_Name and returns a Dir_Type value -- that refers to this directory, and is positioned at the first entry. -- -- Raises Directory_Error if Dir_Name cannot be accessed. In that case -- Dir will be set to Null_Dir. procedure Close (Dir : in out Dir_Type); -- Closes the directory stream refered to by Dir. After calling Close -- Is_Open will return False. Dir will be set to Null_Dir. -- -- Raises Directory_Error if Dir has not be opened (Dir = Null_Dir). function Is_Open (Dir : Dir_Type) return Boolean; -- Returns True if Dir is open, or False otherwise. procedure Read (Dir : in out Dir_Type; Str : out String; Last : out Natural); -- Reads the next entry from the directory and sets Str to the name -- of that entry. Last is the index in Str such that Str (Last) is the -- last character written. Last is 0 when there is no more file in the -- directory. If Str is too small for the file name, the file name will -- be truncated before beeing copied to Str. The list of files returned -- includes directories in systems providing a hierarchical directory -- structure, including . (the current directory) and .. (the parent -- directory) in systems providing these entries. The directory is -- returned in target-OS form. -- -- Raises Directory_Error if Dir has not be opened (Dir = Null_Dir). function Read_Is_Thread_Safe return Boolean; -- Indicates if procedure Read is thread safe. On systems where the -- target system supports this functionality, Read is thread safe, -- and this function returns True (e.g. this will be the case on any -- Unix or Unix-like system providing a correct implementation of the -- function readdir_r). If the system cannot provide a thread safe -- implementation of Read, then this function returns False. private type Dir_Type_Value; type Dir_Type is access Dir_Type_Value; Null_Dir : constant Dir_Type := null; end GNAT.Directory_Operations; **************************************************************** From: Randy Brukardt Sent: Wednesday, October 04, 2000 10:23 PM I understand (but disagree) with the rest of your comment. But I am puzzled by a couple of things: > Incidentally, in GNAT, we only make things part of the Ada hierarchy if > we think they are possible candidates for such treatment. Well, an implementation isn't allowed to add children of Ada, so GNAT couldn't do that even if it made sense. > However, if this *is* an area for standardization, > I think that the minimal richness is what is found in the > GNAT unit, and I find the proposed Ada child too bare. The proposal covers virtually all of the functionality of the GNAT package (with some differences), and adds quite a bit of functionality that the GNAT package does not have. So I don't know where you get "bare" from. I looked at the GNAT spec before making the proposal, and used all of the ideas from it that I could. > Here is the GNAT spec: ... > subtype Dir_Name_Str is String; I left out the subtype because it didn't seem to buy anything. The description of the meaning of the string had to be left out because it wouldn't be appropriate in the standard. > type Dir_Type is limited private; This is part of the "open" version of directory searching. Claw has both, but I think an iterator version would be better suited for standardization. (I gave the reasons in the notes at the end of the proposal.) > Null_Dir : constant Dir_Type; Don't need this if you don't need the above type. > Directory_Error : exception; I suggested using IO_Exceptions for this, as these are IO operations. Moreover, we get some of the documentation requirements and definitions for free in that case, otherwise we have to write them up specifically. > procedure Change_Dir (Dir_Name : Dir_Name_Str); Called Set_Current_Directory in the proposal. "Dir" is an abbreviation, and the Ada standard doesn't like abbreviations. If people think "Directory" is too long, we could change that to "Folder". > procedure Make_Dir (Dir_Name : Dir_Name_Str); Called Create_Directory in the proposal. > function Get_Current_Dir return Dir_Name_Str; Called Get_Current_Directory in the proposal. > procedure Get_Current_Dir (Dir : out Dir_Name_Str; > Last : out Natural); I didn't provide a procedure version of the functions, but if that is thought to be important, its easy to add. > procedure Open (Dir : out Dir_Type; Dir_Name : in Dir_Name_Str); Part of the "Open" file searching. Use the iterator For_Every_Directory_Entry instead. > procedure Close (Dir : in out Dir_Type); Also part of the "Open" file searching. Not necessary, the iterator does it automatically. > function Is_Open (Dir : Dir_Type) return Boolean; Also part of the "Open" file searching. Since there is no object, there is no need for this. > procedure Read > (Dir : in out Dir_Type; > Str : out String; > Last : out Natural); Also part of the "Open" file searching. The "Action" procedure and the "Name" function takes the place of this. > function Read_Is_Thread_Safe return Boolean; This seems too system-specific to be part of the standard. The proposal, OTOH, offers filtered searching and the capability of retrieving properties beyond just the name, which are quite useful in my experience. **************************************************************** From: Pascal Leroy Sent: Thursday, October 05, 2000 3:13 AM > I am dubious about trying to standardize functionality of this kind at > this stage. I agree with Robert. First, I am not sure that there are many applications out there that need to run on both Unix and Windows (I don't know of any in our installed base; I know of people migrating from Unix to NT, but that's a different story altogether). Moreover, anyone designing such an application will be better off encapsulating the directory services that they need (and many other OS services, btw) than relying on an interface that will necessarily be a least common denominator. More importantly, we must make the best use of the scare ARG resources. While a package of directory services would be nice-to-have, it's easy for users to write their own, so it's not like anyone is stuck waiting for the ARG to come up with a solution. On the other hand, there are amendment proposals on the table that deal with real language holes which cannot be circumvented in a reasonably simple fashion. We have add this discussion already when we did the TC: let's focus on the 20% of the issues that will benefit 80% of the users. **************************************************************** From: Pascal Leroy Sent: Thursday, October 05, 2000 3:02 AM > Well, an implementation isn't allowed to add children of Ada, so GNAT > couldn't do that even if it made sense. WHAT?! Hopefully this statement is incorrect, or else the RM needs to be changed. As far as I can tell, even the _user_ can add children of Ada (well, they better know what they are doing, but still they can). **************************************************************** From: Robert Dewar Sent: Thursday, October 05, 2000 10:23 AM <<> Incidentally, in GNAT, we only make things part of the Ada hierarchy if > we think they are possible candidates for such treatment. Well, an implementation isn't allowed to add children of Ada, so GNAT couldn't do that even if it made sense. >> But it is allowed to add grandchildren! **************************************************************** From: Michael Yoder Sent: Thursday, October 05, 2000 9:27 AM I assume "Claw.Directories.Everything" should be replaced by "Everything." **************************************************************** From: David C. Hoos, Sr. Sent: Thursday, October 05, 2000 5:03 AM I have six comments about the proposal, viz.: 1. I like the proposal, in general. 2. There is an obvious error where "Claw.Directories.Everything" should simply be "Everything" 3. The use of verb phrases for function names is not recommended by most (if not all) standards. Therefore I would recommend Get_Current_Directory be named Current_Directory, instead. 4. The function "Name_Of" should be named simply "Name", for consistency (e.g.) with Ada.Text_IO. 5. The function "Is_Ordinary_File" adds yet another term for something that has had a different name for many years. I suggest that that function be named "Is_Regular_File." 6. The word "Directory" in the names of the generic iterators is redundant -- after all, the package is named "Directories," and "Directory" is the formal names for a parameter of these iterators. Long names (i.e. no cryptic abbreviations) are useful, up to the point where additional words in the name add no new information. I was puzzled by your statement that "The POSIX libraries .... leav[e] out ... even Linux." The florist POSIX/Ada bindings have been available for Linux for several years. Even so, the point about those OSs left out is well-taken, and having this functionality as a child of Ada is a good idea, in my view. **************************************************************** From: jj@ddci.dk Sent: Thursday, October 05, 2000 7:38 AM It is illegal for the user (in standard mode), see [AARM A.2(4)] but I don't see how it should be illegal for the implementation. **************************************************************** From: Randy Brukardt Sent: Thursday, October 05, 2000 1:27 PM A couple of responses to comments: Pascal said: > First, I am not sure that there are many applications out > there that need to run on both Unix and Windows (I don't know of any > in our installed base; I know of people migrating from Unix to NT, > but that's a different story altogether). Well, most of the ones I've worked on do in fact run on both. The Janus/Ada compiler uses these sort of operations for library management. The mini-web server uses them to provide download listings. Of course, in both cases, this isn't the *only* non-portable operation. Pascal continues: > While a package of directory services would be nice-to-have, > it's easy for users to write their own, so it's not like anyone is stuck > waiting for the ARG to come up with a solution. I think "easy" is the wrong word here, "possible" is more like it. Doing so requires fairly detailed understanding the API of the OS. However, that really isn't the point. The point is that (almost?) every Ada compiler provides this functionality (so it must be important), but they all do it differently. It is this sort of needless difference that makes real-world Ada programs less portable than they otherwise would have to be. > On the other hand, there are amendment proposals on the table that > deal with real language holes which cannot be circumvented in a > reasonably simple fashion. Certainly, and I would be the first to say that those should be dealt with first. But there aren't that many critical issues (thanks to Tucker's excellent job that last time around), almost everything proposed falls into the "nice to have" category. You can't convince me that "Is_Null_Occurrence" or even 'Object_Size is any more important than this. Robert Dewar said (responding to me): ><couldn't do that even if it made sense.>> >But it is allowed to add grandchildren! Yes, of course. But for the sort of significant package (such as this one), making it a grandchild of some vaguely related package doesn't make a lot of sense. Certainly, Ada.Text_IO.Directories or Ada.IO_Exceptions.Directories does not seem like a good idea; so I'm not surprised that the GNAT designers did not include this functionality in Ada. David Hoos noted a couple of errors in the proposal. I had changed "Name_of" to just "Name", but obviously missed the most important place. It's obvious that I didn't compile this package (as it is intended as a trial ballon, so I didn't want to spend too long on it). **************************************************************** From: Tucker Taft Sent: Thursday, October 05, 2000 1:02 PM Randy Brukardt wrote: > ... > Therefore, I am proposing a package Ada.Directories. This package is based > on the existing Claw package (Claw.Directories) [which was designed for > Microsoft Windows only], the Ada POSIX bindings, and the GNAT package > GNAT.Directory_Operations. > ... > Comments, brickbats welcome. I think this is an important area for standardization. I am surprised that others have not repeatedly faced the issue of moving code between Unix and NT (and Mac for some of us ;-). Certainly all the server-side code we develop these days has to run on both Unix and Win2K/NT. Even if it runs on only Unix, having to go to the Posix interface to get these basic capabilities seems like overkill, especially given that basic File I/O is part of the Ada library. **************************************************************** From: Robert Dewar Sent: Thursday, October 05, 2000 11:05 AM For GNAT, you cannot add children to Ada unless you use a special implementors-only switch (this switch of course results in non-standard behavior). And you cannot add grandchildren either without this special switch (we take full advantage of the RM permission to restrict all user additions to standard hierarchies). By the way, the RM specifically expects there to be modes to control this, and the relevant RM restriction is: 4 In the standard mode, it is illegal to compile a child of package Ada. which says nothing about grandchildren, and this omission is quite deliberate (or at least I have always assumed it is, since it is really quite important for an implementation to be able to add grandchildren). <> Well you may want to change it, but you have had five years to make that comment, and have not done so, so it is a bit late now to make a late and very big change to the language! What possible benefit would there be in changing the language at this stage. Sounds like you just did not read the RM carefully here :-) :-) Note that the deliberateness of the decision here can be assessed by the following paras in the AARM: 4.a Reason: The intention is that mentioning, say, Ada.Text_IO in a with_clause is guaranteed (at least in the standard mode) to refer to the standard version of Ada.Text_IO. The user can compile a root library unit Text_IO that has no relation to the standard version of Text_IO. 4.b Ramification: Note that Ada can have non-language-defined grandchildren, assuming the implementation allows it. Also, packages System and Interfaces can have children, assuming the implementation allows it. 4.c Implementation Note: An implementation will typically support a nonstandard mode in which compiling the language defined library units is allowed. Whether or not this mode is made available to users is up to the implementer. 4.d An implementation could theoretically have private children of Ada, since that would be semantically neutral. However, a programmer cannot compile such a library unit. So I don't think there is an issue here, merely some language design points which Pascal had overlooked :-) **************************************************************** From: Robert Dewar Sent: Thursday, October 05, 2000 11:00 AM <> Well I have to comment that we have many users who are working on applications that have to work on both Unix and NT, but then this is something that GNAT works hard to accomodate, so the difference is not surprising. Actually note that many libraries, as opposed to applications have to solve this problem (in contexts other than GNAT, including I am sure most or all other Ada 95 compilers). For example, CLAW is a library that must work on NT and Unix, even if a given app does not. Still I think it will be relatively difficult to proceed in standardizing this, although reading Randy's latest message makes me more sympathetic to the effort (after all this is something any C programmer can deal with in a portable manner, why should it be non-portable in Ada?) And regarding the last sentence, the whole point in portable libraries is to depend on the least common denominator if you want to be portable. Also, migration is NOT such a "different story altogether". By making this portable, you eliminate one more task involved in such migration. Of course in practice all vendors have decent solutions to this problem (at least I would assume that is the case). The question is whether it is worth trying to abstract a common approach, and I must say I am somewhere in the middle on this issue at this stage, so let's see what other people think. **************************************************************** From: Pascal Leroy Sent: Thursday, October 05, 2000 3:35 PM > So I don't think there is an issue here, merely some language design > points which Pascal had overlooked :-) Surely I was wrong when I said that users can compile a child of Ada: that's clearly forbidden by RM95 A.2(4). However, I don't see that this paragraph forbids implementation-defined children of Ada. I can't find a requirement that says "implementation-defined units have to be compiled in the standard mode". Surely an implementation-defined child of Ada will not be compiled in the standard mode, but that's part of the black magic that takes place when the implementers prepare their predefined libraries. (To be honest, I may have a biased view here because we use a library-based compilation model; not sure how that works with a source-based model.) **************************************************************** From: Pascal Leroy Sent: Thursday, October 05, 2000 3:42 PM > > On the other hand, there are amendment proposals on the table that > > deal with real language holes which cannot be circumvented in a > > reasonably simple fashion. > > Certainly, and I would be the first to say that those should be dealt with > first. But there aren't that many critical issues (thanks to Tucker's > excellent job that last time around), almost everything proposed falls into > the "nice to have" category. You can't convince me that "Is_Null_Occurrence" > or even 'Object_Size is any more important than this. Mutually-dependent types, unchecked-union, access-to-constant parameters, access type conversions, meaning of Bit_Order, revised rules for dispatching, T'Class as generic actual: these are much more important issues IMHO than taking a random package and trying to standardize it. **************************************************************** From: Michael Yoder Sent: Thursday, October 05, 2000 2:21 PM Pascal wrote: >More importantly, we must make the best use of the scare ARG resources. >While a package of directory services would be nice-to-have, it's easy for >users to write their own, so it's not like anyone is stuck waiting for the >ARG to come up with a solution. On the other hand, there are amendment >proposals on the table that deal with real language holes which cannot be >circumvented in a reasonably simple fashion. Pascal wrote: >We have add this discussion already when we did the TC: let's focus on the >20% of the issues that will benefit 80% of the users. > >Pascal I agree with Pascal's sentiment but I may disagree with where this issue lands. I would say lack of simple directory operations has the most common annoying impediment to my programming for quite some time. (The systems for which I write common code are VMS and Unix.) That is, my current belief is that this *is* worth the use of scarce resources; it isn't a strongly held opinion. It seems to me that even if this were low payoff, it's also low effort, which may make it worthwhile regardless. **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, October 05, 2000 12:29 PM From: "Randy Brukardt" >[...] > Therefore, I am proposing a package Ada.Directories. This package is based > on the existing Claw package (Claw.Directories) [which was designed for > Microsoft Windows only], the Ada POSIX bindings, and the GNAT package > GNAT.Directory_Operations. > This is certainly a good idea - I had it also ;-) . May I suggest that you have a look at package OS_Services which is available from Adalog's components page (http://pro.wanadoo.fr/adalog/compo2.htm). I take a different approach to scanning and obtaining information about directory entries. One of the main ideas of my approach was to set up a structure that can provide OS specific services without affecting portability for programs that don't use them, i.e. don't restrict to a least common denominator. Maybe we cannot come up with a solution that takes the best of both worlds. **************************************************************** From: Robert Dewar Sent: Thursday, October 05, 2000 7:18 PM <> Well I must say that our impression from users is that they are quite happy to use the package we provide, and we do not have the impression that this is a significant problem. **************************************************************** From: Robert Dewar Sent: Thursday, October 05, 2000 7:15 PM <> That's a real stretch. Look at the paragraphs in the AARM 4.a Reason: The intention is that mentioning, say, Ada.Text_IO in a with_clause is guaranteed (at least in the standard mode) to refer to the standard version of Ada.Text_IO. The user can compile a root library unit Text_IO that has no relation to the standard version of Text_IO. 4.b Ramification: Note that Ada can have non-language-defined grandchildren, assuming the implementation allows it. Also, packages System and Interfaces can have children, assuming the implementation allows it. This pretty clearly implies that Ada can NOT have non-language-defined children, and this is surely the intent. <> There is absolutely no difference here that results from the compilation model. In either case, there needs to be a special mechanism for adding children to Ada. But I think it would be quite wrong for an implementor to provide implementation defined children of Ada. Incidentally, I completely misunderstood Pascal's position here, I thought he was arguing AGAINST implementation defined grandchildren, and in fact he appears to be arguing FOR implementation defined children. **************************************************************** From: Robert A Duff Sent: Friday, October 06, 2000 9:02 AM Robert wrote: > This pretty clearly implies that Ada can NOT have non-language-defined > children, and this is surely the intent. Yes, that was the intent, as far as I recall. **************************************************************** From: Robert A Duff Sent: Friday, October 06, 2000 9:06 AM Well, I can't resist making one somewhat-technical comment: Randy wrote: > Called Set_Current_Directory in the proposal. "Dir" is an abbreviation, > and the Ada standard doesn't like abbreviations. If people think "Directory" > is too long, we could change that to "Folder". I don't think we have to avoid the "Dir" abbreviation here. It's no worse than the "IO" in "Text_IO". I could live with "Directory", too. I really, really hate the term "Folder". **************************************************************** From: Robert A Duff Sent: Friday, October 06, 2000 9:01 AM I have lots of technical comments on Randy's proposal. However, I think we should first decide whether (at least tentatively) whether to standardize this stuff at all. I'm (mildly) in favor of it. Can we argue about that first, and perhaps have a straw poll? I don't want to waste time arguing about the technical points if only two people are interested. Pascal, knowing that priorities are not absolute, but are relative to each other, wrote: > Mutually-dependent types, unchecked-union, access-to-constant parameters, > access type conversions, meaning of Bit_Order, revised rules for > dispatching, T'Class as generic actual: these are much more important issues > IMHO than taking a random package and trying to standardize it. I agree that the following are higher priority than directory ops: mutually-dependent types, access-to-constant parameters, access type conversions, T'Class as generic actual. I don't agree about unchecked-union or meaning of Bit_Order. I don't remember what "revised rules for dispatching" means. :-( On my list, "mutually-dependent types" is highest priority. Mike Yoder wrote: > That is, my current belief is that this *is* worth the use of scarce > resources; it isn't a strongly held opinion. It seems to me that even if > this were low payoff, it's also low effort, which may make it worthwhile > regardless. Perhaps. But I'll bet we could spend a LOT of time arguing about stylistic details. Also, whether to add one more "nice-to-have" operation to the package. **************************************************************** From: Pascal Leroy Sent: Friday, October 06, 2000 2:57 AM > It seems to me that even if > this were low payoff, it's also low effort, which may make it worthwhile > regardless. There are really three "efforts" involved here: 1 - The effort needed to standardize this package and its semantics. 2 - The effort needed to write ACAATS tests to ensure that implementers comply with the specification. 3 - The effort needed to implement the package. I'm sure that #3 is a very small effort; it really looks like an afternoon project to me. Regarding #1: the ARG has demonstrated a surprising ability to argue for hours about totally trivial details; in fact, it seems that the more trivial the issue, the more arguments it generates because everybody understands the issue and has a strong opinion. Regarding #2, the ARG has not been very good at producing tests for new or modified capabilities. The only significant effort in that direction were the tests that Bob Duff wrote in the ACAATS 2.2 timeframe. My gut feeling is that 60% of the changes that went into the corrigendum have no corresponding test. My conclusion is that I'd rather devote precious ARG-hours to other topics, or to writing ACAATS tests for old AIs. **************************************************************** From: Pascal Leroy Sent: Friday, October 06, 2000 2:48 AM > < children of Ada. I can't find a requirement that says > "implementation-defined units have to be compiled in the standard mode".>> > > That's a real stretch. Look at the paragraphs in the AARM > > 4.a Reason: The intention is that mentioning, say, Ada.Text_IO in > a with_clause is guaranteed (at least in the standard mode) to refer > to the standard version of Ada.Text_IO. The user can compile a root > library unit Text_IO that has no relation to the standard version of > Text_IO. I understand the language design principle of preventing a random user from unwittingly compiling a package named Ada.Text_IO, thereby resulting in plague and pestilence. (Note that you would hope that this same user would be prevented from compiling a package named Text_IO, too, but she isn't.) I don't see why it's beneficial to prevent an implementation from providing a package named Ada.Some_Useful_Services. Especially when it's OK for them to provide a root library unit named Some_Useful_Services (which causes name-space pollution) or a grandchild of Ada named Ada.Tags.Some_Useful_Services (as is illustrated by the case of directory services, there are situations where there is no good place to put a grandchild of Ada). > This pretty clearly implies that Ada can NOT have non-language-defined > children, and this is surely the intent. Even if I followed Robert's logic (and I cannot be too much convinced at this point) I don't see why it's good. > Incidentally, I completely misunderstood Pascal's position here, I thought > he was arguing AGAINST implementation defined grandchildren, and in fact > he appears to be arguing FOR implementation defined children. Correct. **************************************************************** From: Christoph Grein Sent: Friday, October 06, 2000 1:39 AM Robert Dewar wrote: ... > Of course in practice all vendors have decent solutions to this problem > (at least I would assume that is the case). The question is whether it > is worth trying to abstract a common approach, and I must say I am > somewhere in the middle on this issue at this stage, so let's see what > other people think. Is this an invitation on a poll? If so, I say it's worth the effort, even if it provides onlu the least commeon denominator. What I'm missing and which I think is fundamental is a requirment like function Name (Directory_Entry : in Directory_Entry_Type) return String; Returns the external name of the file, directory, or other item represented by Directory_Entry. The format of the name returned is implementation-defined. The exception Use_Error is propagated if Directory_Entry is invalid. => The returned string shall be usable for Ada.Text_IO file operations if Is_Ordinary_File is true for the given Directory_Entry (perhaps after an appropriate call to Set_Current_Directory). As yet IMHO the interaction with Ada.Text_IO is completely unspecified. **************************************************************** From: Robert Dewar Sent: Friday, October 06, 2000 10:35 AM <> I agree, the term folder would be merely an obfuscation in this environment (perhaps Randy prefers "transput" to "input-output" since it is shorter :-) **************************************************************** From: Robert Dewar Sent: Friday, October 06, 2000 10:39 AM <> Interesting, shows how the view of a language designer can be different from the view from the applications end. I would never have included access-to-constant parameters, or access type conversions, or T'Class as generic actual on my list of important things. These are simply nice to have, but are not causing anyone any significant troubles in the field as far as I can see. Yes, mutually-dependent types are significant, but in our experience, only in connection with Java environments so far. The meaning of Bit_Order on the other hand is quite important, and certainly we implemented this in GNAT because there was significant demand. Whether or not other compilers standardize this as required is of course not so signiicant to us. Similarly, we could not begin to survive without Unchecked_Union. that's an absolutely vital capability for some of the existing C bindings. From a usage point of view, having access to directory operations is also absolutely vital. An Ada compiler not providing this would be crippled from the point of view of many users. Whether this means it should be standardized is another matter. Robert Dewar (speaking now as a compiler vendor, and not as a language designer :-) **************************************************************** From: Pascal Leroy Sent: Friday, October 06, 2000 10:42 AM > Pascal, knowing that priorities are not absolute, but are relative to > each other, wrote: > > > Mutually-dependent types, unchecked-union, access-to-constant parameters, > > access type conversions, meaning of Bit_Order, revised rules for > > dispatching, T'Class as generic actual: these are much more important issues > > IMHO than taking a random package and trying to standardize it. > > I agree that the following are higher priority than directory ops: > mutually-dependent types, access-to-constant parameters, access type > conversions, T'Class as generic actual. > I don't agree about unchecked-union or meaning of Bit_Order. > I don't remember what "revised rules for dispatching" means. :-( By "revised rules for dispatching" I was alluding to AI-00232. > On my list, "mutually-dependent types" is highest priority. On my list, mutually-dependent types and unchecked-unions are the highest priority items. I could live without T'Class as generic actual. Anyway, maybe a first step would be to look at the amendment AIs that are on the table, and take a straw vote regarding their priority. The !priority of the AIs have been assigned by the editor (Bob and Randy) and presumably reflected their own feeling. However it would be good to know what topics are considered high-priority by the ARG as a whole. This is especially important for amendment AIs, as they are likely to require considerably more time than the AIs that merely plug holes in the RM. **************************************************************** From: Robert Dewar Sent: Friday, October 06, 2000 10:58 AM <> Well Pascal is free to second guess a clear design intention, but as I say, to change the language here would require a lot of convincing. Personally, I feel that adding implementation dependent first level children to Ada would be a horrible idea. Users need to know that packages in Ada are part of the RM and portable. Yes, I realize that this argument would apply to grand-children as well, but there was a concious decision to compromise between two conflicting requirements here. I think that implementations should be very judicious in adding new packages to the Ada hierarchy, even at the grandchild level. I certainly do not want to encourage profligacy here by allowing children to be added. For instance I would think it just horrible if we had added Ada.SPITBOL, rather than GNAT.SPITBOL to our distribution. Note that the permission in RM (A.3(27)) is interesting: (ooops, I mean A.3.3(27)) 27 An implementation may provide additional packages as children of Ada.Characters, to declare names for the symbols of the local character set or other character sets. Now of course, under all our interpretations, this implementation permission is junk, since everyone agrees you can add grandchildren anyway, but the fact that the authors state this explicitly as implementatoin permission (rather than implementation advice that such packages might be appropriate) is helpful in understanding the general viewpoint here. **************************************************************** From: Robert Dewar Sent: Friday, October 06, 2000 11:07 AM <> OK, I agree with this assessment. Indeed mutually dependent types and unchecked unions are language extension (yes yes, I know that technically unchecked union is not an extension :-) tha we provide in GNAT, and which both our own technology and many of our users programs depend. In the case of WITH TYPE, we just had to go ahead with our own design (which is simpler than the full bells-and-whistles proposal from Tucker) in the absence of an agreed solution, because we absolutely required this for our Java related work. As I said earlier, unchecked union also is quite critical (again we find Tucker's version to have unnecessary bells and whistles, and prefer a simpler approach :-) **************************************************************** From: Randy Brukardt Sent: Friday, October 06, 2000 11:52 AM > Pascal, knowing that priorities are not absolute, but are relative to > each other, wrote: > > > Mutually-dependent types, unchecked-union, access-to-constant parameters, > > access type conversions, meaning of Bit_Order, revised rules for > > dispatching, T'Class as generic actual: these are much more important issues > > IMHO than taking a random package and trying to standardize it. To follow up on these messages, I would rank the issues in this order: AI-0217 (Mutually dependent types) AI-0230 (implicit conversions between access types) AI-0222 (Feature control) AI-0224 (pragma Unsuppress) AI-0218 (Accidental overriding) [Unassigned] {Directory operations} [Unassigned] {Other packages all compilers provide.:-)} [Unassigned] {'Object_Size} AI-0216 (Unchecked unions) AI-0231 (access-to-constant parameters) AI-0241 (Testing for Null_Occurrence) AI-0232 (Dispatching operation visibility and ambiguity) AI-0234 (Unsigned integer types) I don't think "meaning of Bit_Order" is an amendment. (AI-0133 is classed as a BI). Similarly with "T'Class as a generic actual type" (AI-0158 is also classed as a BI). I can believe that the latter is misclassed. I think the last two on the list are bad ideas that will never go anywhere, thus I gave them the lowest priority. There probably are other things that ought to be added to this list, but those are the ones we have on the table at the moment. We'd probably benefit from a general discussion of how we'll approach this at the upcoming meeting. **************************************************************** From: Randy Brukardt Sent: Friday, October 06, 2000 4:42 PM > Robert wrote: > > > This pretty clearly implies that Ada can NOT have non-language-defined > > children, and this is surely the intent. And Bob replied: > Yes, that was the intent, as far as I recall. Randy (who remembers trivia like this, but forgets to check URLs before sending important announcements...) recalls: Ada 9X originally had a requirement that a child of Ada could be compiled only if its name matched a language-defined package. That requirement was hard to implement (I remember that we did implement it as part of the UI contracts; we had to look up the name of the unit against a list - yuck), and still allowed problems, so it was changed to the requirement that now exists. Certainly, the history of requirement was that no non-language-defined children of Ada be allowed; the requirement was written more weakly just so that it was easier to implement. **************************************************************** From: Jean-Pierre Rosen Sent: Saturday, October 07, 2000 12:20 AM > I have lots of technical comments on Randy's proposal. However, I think > we should first decide whether (at least tentatively) whether to > standardize this stuff at all. > It always puzzles me to see that the Ada community refuses to see that anything can exist before it is rubber-stamped by ISO. After all what made the success of Java ? That Sun very rapidly pushed lots and lots of components - even at the cost of quality, see the number of deprecated features. If we (an informal group) can agree on a sufficiently useful package, and can get compiler vendors to provide the package, that's good enough from a user's point of view. If the package gets widely used, it can make its way in an annex for the next revision. No need to waste ARG time on this. After all, that's what the PAS way at ISO is. **************************************************************** From: Robert A Duff Sent: Tuesday, October 10, 2000 11:55 AM Robert wrote: > The meaning of Bit_Order on the other hand is quite important, and > certainly we implemented this in GNAT because there was significant > demand. Whether or not other compilers standardize this as required > is of course not so signiicant to us. > > Similarly, we could not begin to survive without Unchecked_Union. > that's an absolutely vital capability for some of the existing C > bindings. OK, I guess find the above convincing. > I think that implementations should be very judicious in adding new > packages to the Ada hierarchy, even at the grandchild level. I agree. The reason for allowing grandchildren is that the implementation might want to extend an existing (language-defined) abstraction, so it ought to be a child of that thing, especially if it needs to know about the private part. Pascal wrote: > I don't see why it's beneficial to prevent an implementation from providing > a package named Ada.Some_Useful_Services. Especially when it's OK for them > to provide a root library unit named Some_Useful_Services (which causes > name-space pollution) ... Well, the top-level Some_Useful_Services doesn't really pollute the namespace, because users can just compile their own thing called Some_Useful_Services. Anyway, it's logically impossible to forbid compiler vendors from providing Some_Useful_Services. > (Note that you would hope that this same user would > be prevented from compiling a package named Text_IO, too, but she > isn't.) As I recall, some people believed (during the 9X process) that this was a feature -- that you can have your own version of Text_IO. I wouldn't do that myself. But it was intentional that this be allowed. Robert wrote: > This by the way is Parkinson's Second Law: The time spent discussing an > issue is inversely proportional to its importance. I remember one time when the ARG spent about 20 minutes deciding whether to take a 15-minute coffee break *now*, versus after doing one more AI. I believe John Barnes got up and left the room muttering under his breath. ;-) **************************************************************** From: David Emery Sent: Tuesday, October 10, 2000 1:00 PM Granted, I'm biased in this respect, but I'd really suggest that we could reuse the existing POSIX directories stuff. The key thing is adding the "spin" on how this works in Windows. I think it would be A Very Bad Thing for Ada to have something that does not directly align with the existing (and widely used) POSIX API. Incidentally, a problem I see with Randy's proposal is that it's very ambiguous with respect to things in the file namespace that are not "files" or "directories". POSIX recognizes lots of stuff in the file namespace, including character-devices, block-devices, semaphores, shared memory segments, etc. **************************************************************** From: bjkae@infomatics.saab.se Sent: Tuesday, October 10, 2000 1:07 PM Straw poll vote: I am in favor of standardizing directory operations. Standardizing trivial but commonly used operations is very useful. Ada83 probably suffered a lot as a number of these packages were lacking: Elementary functions, string operations, command line, etc. Let us continue the Ada95 way of making things easier also for trivial programs. /Björn Källberg **************************************************************** From: Ted Baker Sent: Wednesday, October 11, 2000 5:46 AM As you might expect, I support Dave's suggestion. --Ted **************************************************************** From: David Emery Sent: Wednesday, October 11, 2000 12:22 PM I think it would be A Good Thing if the directory operations could be defined in terms of renames on existing POSIX operations. This will preserve signatures and semantics. For non-POSIX implementations, the implementor can provide the proper bodies, etc. **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, October 12, 2000 2:44 AM > Granted, I'm biased in this respect, but I'd really > suggest that we could reuse the existing POSIX > directories stuff. The key thing is adding the "spin" > on how this works in Windows. I think it would be > A Very Bad Thing for Ada to have something that does > not directly align with the existing (and widely > used) POSIX API. Then, doing anything would be a waste of time. If you like the POSIX interface, by all means, use it! Nothing prevents you from having the same interface on non-POSIX systems. I think the need that has been expressed here is for something that is NOT linked to any special OS. There is a challenge here: allowing to be system-independent while not falling into the least-common-denominator syndrom. This was my main concern when I wrote OS_Services: define a structure that would be extensible to allow access to OS pecularities, AND allow a set of common portable features. If you don't see what I mean, please go to http://pro.wanadoo.fr/adalog/compo2.htm and download OS_Services. There is a full HTML documentation that discusses the issues. **************************************************************** From: David Emery Sent: Thursday, October 12, 2000 12:49 PM We do a major dis-service to the community if we produce a OS-independent package that breaks lots of existing code for no clear benefit. It's clear that there is a lot of similarities between Unix/POSIX, Windows, MacOS, etc in their file systems. Early in the POSIX work I did a study of how far you could implement the POSIX API on MS-DOS (I called it "DOSIX":-) Most of the POSIX file system packages could be implemented with minor "semantic spin". Given that lots of programs are using the POSIX file packages, it makes sense to me to adopt a compatible interface for other systems. This is particularly true since the major porting efforts that I've seen that are not Unix->Unix are Unix->Windows. (And most certainly not Windows->Unix, unfortunately.) Thus I think the community is best served by preserving the POSIX API as much as possible. That's why I suggest that the Directory services be defined so that they could be implemented using "renames" of the existing POSIX services. **************************************************************** From: Tucker Taft Sent: Thursday, October 12, 2000 5:22 PM David Emery wrote: > ... > Thus I think the community is best served by preserving > the POSIX API as much as possible. > > That's why I suggest that the Directory services be > defined so that they could be implemented using > "renames" of the existing POSIX services. This sounds nice, but we should be sure that we aren't forcing some kind of artificial simulation of the Posix path syntax (e.g. blah/blah/blah) onto the Windows, Mac, VMS, etc. implementations. How about posting a sample renaming, and perhaps indicate where there might be a need for some additional functions, constants, etc.? Some of us haven't memorized the Posix interface, and don't have ready access to it. **************************************************************** From: Jean-Pierre Rosen Sent: Friday, October 13, 2000 3:39 AM > We do a major dis-service to the community if we > produce a OS-independent package that breaks > lots of existing code for no clear benefit. I am puzzled by this statement... There IS a Posix binding to Ada, and if a different package appears, it certainly does not mean that all users of POSIX *must* give up on Posix and switch to that package! > [snip] > Thus I think the community is best served by preserving > the POSIX API as much as possible. > > That's why I suggest that the Directory services be > defined so that they could be implemented using > "renames" of the existing POSIX services. What I'm saying is that if there is not enough added value between the proposed package and the Posix interface, then it's not worth the effort. I'm viewing (IMHO) this package as high-level, thick if you wish. I'd rather use a Posix emulation package on Windows (btw it exists thanks to P. Obry) than an "almost Posix" package. **************************************************************** From: Ted Baker Sent: Tuesday, October 17, 2000 4:05 PM | I think the need that has been expressed here is for something | that is NOT linked to any special OS.... The POSIX standard is not linked to any specific OS, any more than Ada is linked to a specific OS. POSIX.1 has been implement not just on a variety of Unix-like systems, but also on Windows NT, VAX VMS, PrimeOS, and other operating systems. **************************************************************** From: Robert A Duff Sent: Tuesday, October 17, 2000 5:26 PM > The POSIX standard is not linked to any specific OS, any more than > Ada is linked to a specific OS. POSIX.1 has been implement not just > on a variety of Unix-like systems, but also on Windows NT, VAX VMS, > PrimeOS, and other operating systems. I would like to hear someone who knows what they're talking about list the reasons, if any, why POSIX is unsuitable as the "directory operations" package we're talking about. Then we can decide "POSIX is good enough; we don't need anything else" or "we need to design something based on POSIX that more fully solves the problem" or "POSIX is irrelevant, so we should design something from scratch (or based on Randy's package or GNAT's package or whatever)". **************************************************************** From: Randy Brukardt Sent: Wednesday, October 18, 2000 12:46 PM > > The POSIX standard is not linked to any specific OS, any more than > > Ada is linked to a specific OS. POSIX.1 has been implement not just > > on a variety of Unix-like systems, but also on Windows NT, VAX VMS, > > PrimeOS, and other operating systems. > > I would like to hear someone who knows what they're talking about list > the reasons, if any, why POSIX is unsuitable as the "directory > operations" package we're talking about. Then we can decide "POSIX is > good enough; we don't need anything else" or "we need to design > something based on POSIX that more fully solves the problem" or "POSIX > is irrelevant, so we should design something from scratch (or based on > Randy's package or GNAT's package or whatever)". I'm not completely sure that I know what I'm talking about here (because I can only find a late draft of the POSIX standard, not a current version), but let me try this anyway. First of all, "Randy's package" is strongly based on the POSIX package. Most of the subprogram names and functions are the same. My proposal is essentially that of Bob's second choice. I don't think starting from scratch makes any sense at all, but that POSIX is not directly a very good choice. Let me enumerate some of the reasons: -- All of the package names start with "POSIX". I think this would be very off-putting to anyone running on a non-POSIX system. This package ought to be a child of Ada, like Text_IO. -- The package we standardize has to be meaningful on any target system. (At least, we have to define what happens when the operations aren't supported.) The POSIX packages, by definition, do not need to deal with this problem. I spent quite of bit of effort to define the package such that it would be meaningful on the largest variety of systems. For instance, it is important that directory searching work even if the underlying system doesn't support user-created subdirectories. -- POSIX exceptions seem to be mapped to the various error codes that a POSIX system can return. For a standard package, a mapping to IO_Exceptions is much better. By doing that, we avoid having to explain again all of the stuff about the meaning of Device_Error and so on. -- Create_Directory takes a POSIX_Permissions parameter. Ada has traditionally used Form parameters for this use, which is why my proposal uses that rather than a "permissions" parameter. I don't think there is any value to standardizing permissions, especially at this late date. -- I used the names "Get_Current_Directory" and "Set_Current_Directory" rather than the POSIX "Change_Working_Directory" and "Get_Current_Working_Directory" because these are the most meaningful to someone who usually works on Windows (i.e. me!). I have no objection to using "Change_Working_Directory" instead of "Set_Current_Directory", but "Get_Current_Working_Directory" is unnecessarily verbose. If we do change to the POSIX terminology, we should at least use "Get_Working_Directory" or just "Working_Directory"; the "current" is redundant. -- The POSIX operations Change_Working_Directory and Get_Current_Working_Directory are in the POSIX_Process_Environment package. While we might want to consider a standard environment package, that is the wrong place for these routines. That reflects the implementation of the current (or working) directory, not the usage of them. In particular, some systems do not sort this information in the environment at all. -- The POSIX directory searching generic function is not designed for extensibility. It doesn't have to be: the target is known. A standard package should, however, be designed so that an implementation can provide child packages to provide whatever other information is available in the directory entry. -- Finally, POSIX does not include any wildcard searching operations, thus forcing each programmer to implement their own. That is in keeping with the POSIX tradition, but it is inappropriate for directory searching on most systems where at least some wildcard processing is part of the operation. Even without it, the POSIX searching generic can be wildly inefficient: it makes one call to the Action routine for every item found. If you are looking only for directories, the vast majority of calls will be false matches. If the underlying system provides any filters at all, returning thousands of false matches is silly. -- POSIX's case sensitivity doesn't bear on this proposal, thank goodness, but it is a primary reason why using POSIX on Windows is a bad idea. Trying to impose case sensitivity on a case insensitive file system leads to many problems, the result being that while POSIX can be used on Windows NT, files created with it are often incompatible with standard Windows applications. (The Windows documentation tells you to avoid the POSIX switches unless you're doing POSIX.) Now, we could design something radically different than POSIX, but I don't think that would be justified. But a direct use of POSIX, which clearly had different goals, especially for portability, than the Ada standard, does not meet the requirements. (If it did, we should simply be using POSIX for this purpose.) **************************************************************** From: Tucker Taft Sent: Friday, December 01, 2000 4:55 PM Randy, You should probably look at JP Rosen's OS_Services package as part of working on the "file/directory services" amendment AI. Reference: http://pro.wanadoo.fr/adalog/compo2.htm **************************************************************** From the minutes of the Leuven meeting (May 18-20, 2001): It was noted that the package seems to be too high in the hierarchy of predefined packages. Randy notes that this package works with all sorts of files, so it would be inappropriate for it to be a child of any specific IO package, and making it a child of IO_Exceptions also seems inappropriate. If Ada 95 had defined a package IO that was the parent of all of the IO packages, then this package could go there, but of course Ada 95 does no such thing. Moreover, we could cause conflicts by defining grandchildren of Ada, but not for children of Ada (since it is illegal to compile children of Ada, and implementors should not define their own, while there are no such restrictions for grandchildren of Ada). Change the name of Get_Current_Directory to Current_Directory. Steve M. suggests that Remove_Directory ought to say that non-empty directories are not deleted. After some discussion, it is suggested to add Create_Tree (which makes all directories needed in the name) and Remove_Tree (delete everything in the named tree, including directories) to the package. Steve's suggestion is adopted. It is also suggested to add Delete (by name [string]) to the package. Rename (Old_Name, New_Name) should also be added. This should be described as changing the name of the file from Old_Name to New_Name. There should be an implementation requirement for this to work at the same level in the same directory (others may raise Use_Error). The Name routine for a directory entry should be split into a Simple_Name and Full_Name routine. Pascal suggests that there should be some way to get the parent directory. First Erhard, and then Steve M. express confusion about how the directory searching iterator works. Randy notes that if Ada experts can't figure out the POSIX iterator mechanism, what chance do users have? It is suggested to add a limited (type) handle searching mechanism, rather than the current iterator scheme. The suggestion is made that the directory searching stuff be put into a separate child unit to avoid clutter. Pascal comments that he doesn't like all these operations taking file names, and that he would prefer a full-fledged Directory_Entry abstraction, much like Text_IO has a File_Type abstraction. There would be an operation to resolve a name to obtain a Directory_Entry, and various operations on Directory_Entries. The group appears unconvinced. A request for file name composition/decomposition functions was made by Tucker. Randy notes that these are very tough to do right. The group would like to see these. Steve M. would like to see a way to create links. Others note that the links on Windows or Mac aren't the same as the links on Unix, so a common definition would be hard. On Sunday, Randy brings up a recent conformity assessment problem, which is somewhat related to this AI. A.8.2(22) says "If an external environment allows alternative specifications of the name (for example, abbreviations), the string returned by the function should correspond to a full specification of the name." The ACATS has always tested this as if it were a requirement. However, some implementations have not been following this requirement (and this fact was overlooked by the testers). Should this "should" be a "shall" (a requirement) or shall it remain a "should" (an Implementation Advice)? There is definitely a serious overhead (it is necessary to call getcwd() on every Open and Create), and there are implementations which have strayed. The best solution is to give users an appropriate directories package, and eliminate this requirement altogether. Randy says that the immediate problem is three ACATS tests (from ACVC 1.11) which require this behavior. With the above solution, the best course of action is to withdraw these tests (they have no other value). The general agreement is that a good directories package makes this requirement unnecessary. We should plan to remove this requirement when a directories package is standardized. No one supported making this a "shall". Randy will integrate this (and associated operations, such as a Simple_Name => Full_Name routine) into the directories package. **************************************************************** From the minutes of the Bloomington meeting (October 5-7, 2001): Once again the motivation for this AI is questioned. Users want it in the standard library and with the Ada POSIX interface in jeopardy, it seems to be necessary. While the discussion of this AI was scattered among the operations and types of the package specification, the minutes have organized the discussion and decisions by how the operations and types were listed: Create_Path (formerly Create_Tree): The name Create_Path better describes the intent of this operation than Create_Tree. A recommendation to add a Boolean parameter for recursion instead of having a separate routine was defeated by a 3-2-5 straw vote. Create the directory with name New_Directory with any necessary enclosing directories. Use the same wording that is found in A.8.2 for files for the raising of exceptions. The last sentence needs to be changed to refer to "any directories" instead of a single directory. The description also should say "zero or more", as this routine does not have to create something. Current_Directory: Is the default directory defined? Not really. Default directory is useful for the use of simple names for directories and files. What it should be is what would be expected from the underlying environment. But Windows has a default directory for each device, which is very different from Unix. Then it should return a full name. Also use the same wording that is found in A.8.2 for files for the raising of exceptions. Set_Directory: Accept simple names (using the default directory as the enclosing directory) or full name. It was during the discussion of this operation that it was determined the terms simple names, full names and default directory need better definitions before the description of operations. (It was noted that portability is best supported through the use of simple names.) Delete_Tree (formerly Remove_Tree): Correct the cut-and-paste error in this description. Use_Error exception is now raised if it is not able to delete the entire tree (or if that tree does not exist). It is noted that when Use_Error is raised, it is possible that some files and/or directories are deleted. A recommendation to add a Boolean parameter for recursion instead of having a separate routine was defeated by a 2-5-3 straw vote. At this point the question was raised as to why use the name Remove_Tree instead of Delete_Tree? The reason given is that most operating systems use the term remove. But remove is inconsistent with the term delete for files in the current language. It was decided to use the term delete, so names Delete_File, Delete_Directory and Delete_Tree will replace Delete (file), Remove_Directory and Remove_Tree. Containing_Directory (formerly Parent_Directory): It was decided to change the name to the new name as the term containing directory is more descriptive of what is being requested. This function will return a full name of the directory of the containing directory. Rename: It was recommended that the Implementation Advice say the Rename function should work when both names are simple names. The discussion brought up the lack of copy operation for files or directories. It was decided to add a Copy_File operation, but no copy directory. Pascal cautions about the difficulties with special files on Unix (and similar beasts on other operating systems). Tuck argues that this shouldn't be a problem for files created by Ada. Copy_File should be defined to copy files that can be created by Ada packages, and to have an implementation-defined behavior for other files. The discussion turned its attention to the search operations and supporting types, leaving the discussion of full and simple names for later. Is_Valid: The need for this operation is questioned. Randy says that it is a useful test for determining if a search produced something meaningful and Bob supported his point. Tuck pointed out that we have the constant No_Directory_Entry for testing of the result of a search, and therefore that operation Is_Valid appears to be redundant. If Is_Valid is retained it should mean the same thing as comparing with No_Directory_Entry. The straw vote on this meaning for Is_Valid was approved, 6-0-4, but the group decided that the redundacy was unpleasant and approved removing Is_Valid by a 7-1-2 straw vote. Note: later in the discussion it was restored! Start_Search and End_Search: The Start_Search operation is an iterator operation on the search object. Consequently Search_Type should be a controlled type to properly support the finalization of search objects. There was a discussion on whether a call on End_Search should be optional. If this is the case, then it means that the finalization of the search object (by leaving the scope of the object, or by starting a new search with the same object) has the effect of calling End_Search. The End_Search subprogram should be retained as a way to release operating system facilities early (i.e., before leaving the enclosing block). The meeting voted to make calls on End_Search optional by a 5-2-3 straw vote. Then there was a recommendation that Start_Search act as a restart, thereby eliminating the need to explicitly call the End_Search operation to restart a search. The group approved making Start_Search act as a restart by a 6-2-2 straw vote. Get_Next_Match: The only significant issue on the Get_Next_Match operation was how it handled an unstarted search object: either return No_Directory_Entry in the Directory_Entry parameter or raise a Status_Error exception. The exception approach is similar to how the existing file operations handle unopened files and was approved by 6-3-1. At the end of the iterator, Get_Next_Match returns No_Directory_Entry. Is_Directory and Is_Ordinary_File: The motivation for distinguishing ordinary files is due to the existence of special files, such as symbolic links to existing files. It was suggested that a more direct way of distinguishing among directories, ordinary files and special files was by defining an enumeration type with these values, such as: type Directory_Entry_Kind is (Directory, Ordinary_File, Special_File); and then change the filter type to be: type Filter_Type is array (Directory_Entry_Kind) of Boolean; Consequently the two functions Is_Directory and Is_Ordinary_File are replaced with a new function Directory_Kind that operates on directory entry objects and returns the new enumeration type. Also the filter parameter in the Start_Search operation is changed. Special files appear to be files that Ada programs can't create or read. Bob believes there are lots of programs that would like to read soft link files in order to find the target. If that is the case may be the best way to handle special files is to provide implementation advice but what is that advice? Does POSIX provide any directions? Not really because it is allowed to deal with all files types that this package doesn't want to handle. It was decided the specifics of special files, such as soft links, should be left to AARM where operating system details can be discussed. Composition of simple and full file names: Randy explained that he borrowed wording for full names from the RM for the Name operation. He further noted that he did consider Jean-Pierre Rosen's documentation on composition, where he divided the composition of file names into these elements: ú Device ú Path ú File ú Extension ú Version This decomposition of file names is meant to be the union of all operating systems. There is a risk that another operating system does not fit this composition. Consequently, he decided against adding any operations to decompose full names. It was noted this list was missing a network element (the host name). Should all of this be avoided by putting the details into a child package? Tuck and Erhard argue that composition of full names for files and directories should be available in this package. Pascal would like to have some access to the composition elements without doing string manipulations to get them from the name strings. The focus is narrowed to Simple_Name function and the composition functions. Tuck argues for just one composition function for either directories or files and let an implementation do the right thing. Portability between systems and implementation is the goal of this interface and that makes the system-specific elements, beyond file and directory names and extensions, difficult to expose. After much discussion, it was decided to take a series of straw votes on how to proceed: ú On whether this package should provide all three kinds of operations, namely compose full file and directory names, extract relative names (the containing directory name or the simple name) and file extension operations (extract simple name or extension and compose file name with simple name and extension), it was defeated by 3-3-4. John commented that he didn't like the notion of making "extension" a first-class citizen. ú The group took a step back to see if there would be agreement on just the compose and relative name operations and it did by 8-0-2 straw vote. ú After taking another straw vote to support more operations (6-3-1), the group supported the addition of just the extension operations (i.e., Base, Extension and Compose_With_Extension) by 5-2-3. Erhard would like to have a (modification) time stamp and size operations. Randy says it is difficult to know what size should be returned, especially in the presence of 64 bit values and to know how to handle time stamps for operating systems that do not produce time stamps. For those operating system with no time stamping, a time stamp operation would raise Use_Error. Consequently the meeting approved time stamp (meaning the time when the file or directory was last modified) by consensus. As for the size of files, it was suggested to let the existing Count type in Stream_IO package (A.12.1) handle this problem. Unfortunately its bounds may not be appropriate to deal with very large files. It was decided not to change the type of count in A.12.1 to be an implementation-defined integer type. Instead a new signed type should be declared in Directory_Operations; the upper bound is implementation-defined and the Size function returns values of this type or raises Constraint_Error if the size is an illegal value for the implementation. The reason why the group eventually decided on a signed type is because operations on Size values are common, and we don't want modular semantics for them. It is implementation defined what the Size function produces for directories or special files. All query functions, such as Kind, Modification_Time or Size, should be applicable to both names and Directory_Entry_Type values. The term "same containing directory" should replace "same directory location" in the Implementation Advice on Rename paragraph and the AARM ramification paragraph should be dropped. Finally the discussion ended with debating whether Directory_Entry_Type should be a limited type, since there appears to be no need for its assignment. This would simplify the implementation and it would be treated similarly to how files are treated. If assignment is needed, then the program should store a pointer to it. Consequently this means the constant No_Directory_entry is no longer needed and the Is_Valid function is restored! Vote on Intent for all these changes: 7-1-1. **************************************************************** From: Randy Brukardt Sent: Monday, January 7, 2002 7:55 PM Package Ada.Directories (AI-248) contains a basic set of file composition/decomposition functions. The only area that I had trouble with in writing up the current design was with the composition function(s). There were a variety of alternatives, which I narrowed down to two: A single Compose function that returns a file or directory name; Separate Compose_Directory and Compose_File_Name functions that return the item specified. I choose the first design, because it is consistent with the behavior of the decomposition functions. For instance, Simple_Name can extract a simple name from either a file name or directory name. However, there is a possible problem with this choice: if there exists an operating system for which file names and directory names are different syntactically, then the single Compose will not work. That's because this is intended to be a purely syntactical function, and it does not require the files or directories to work -- thus it couldn't tell which to create. Is this a real concern? (I can't think of any systems with that problem, but I'm not familiar with them all!) If so, I'll change the proposal to use the second alternative. **************************************************************** From: Tucker Taft Sent: Tuesday, January 8, 2002 12:40 AM On VMS, directories and files have significantly different syntax. However, if the "simple_name" that is returned/expected by decompose/compose retains the special directory syntax when it refers to a directory, this would not be a problem. That is, a "simple_name" in "[]" on VMS would be a directory, whereas a "simple_name" not enclosed in "[]" would be a file. So I guess the answer is that the simple_name's syntax must identify it as a file or a directory, if there is a syntactic difference. If that rule is unpleasant, and there is a desire that "simple_name"s be simple unadorned strings, then you would seem to need two decomposition and two composition functions. **************************************************************** From: Nick Roberts Sent: Saturday, January 19, 2002 1:48 PM First, my thanks to Randy for his sharpness in spotting that the original version of this comment (which has been deleted) was based on an outdated version of the AI. Well done Randy. I have since corrected this, and self-administered 20 lashes of the birch in penitance. My comments in reply to Randy's question (quoted below, hope this doesn't offend) and pertaining to AI-00248-04 are as follows. (1) It would seem particularly apt to accompany the introduction of this package with another, perhaps Ada.Environment, which would provide for the interrogation of the 'environment strings' that many operating systems use to communicate information to programs. E.g.: package Ada.Environment is function Get (Name : in [Wide_]String) return [Wide_]String; function Environment_Name return [Wide_]String; function Environment_Version return [Wide_]String; end Ada.Environment; For the Get function, if an environment variable with the requested Name exists, its value (also a string) is returned, otherwise a null string is returned. The Environment_Name function is intended to return a broad identification of the program's execution environment, e.g. the basic name of an operating system or RTS. The Environment_Version function is intended to give a more precise identification of the same. Possible examples of the results of Environment_Name and Environment_Version might be: "Linux" and "2.4 (Red Hat 6.2)"; "Windows" and "NT 4.0"; "BSD" and "4.4"; "OS/2" and "4.0". Admittedly there are a lot of extra possibilities for this package, which I'll defer to another comment. (2) I think Wide_String should be used throughout Ada.Command_Line, Ada.Environment, and Ada.Directories (instead of String). Doing so would head off a lot of internationalisation [should we use 'i18n' for this word in future?] problems for the future. I believe making this change for (the existing package) Ada.Command_Line would cause fewer problems than it would avoid, in the long term. (3) It was presumably a little mistake to use the name Set_Current_Directory (instead of Set_Directory) in the description of the Current_Directory function. In any case, I would actually prefer the name Set_Current_Directory for the Set_Directory procedure. (4) With regard to Create_Path, I would prefer the standard mandated the behaviour that if any Create (or Create_Directory) - in Ada.Directories or any of the *_IO packages - names a directory within its path which does not exist, that directory should be automatically created. There would then be no need for Create_Path. This would presumably cause extra work for the implementor of the standard Ada *_IO packages, but I think it would be worthwhile, especially since it is something that many application programs would have to do anyway. It seems to me that, in a situation where a directory somewhere along the path of a file (or directory) name does not exist, simply creating the necessary directory would allow the program to continue correctly with its intended operation, and be very unlikely to do any significant harm. On the other hand, raising an exception will stymie the program, at least to some extent. Catching this exception, finding out which directory does not exist, creating the directory, and then trying again is a lot of bother to foist onto the application programmer (and done much more easily and efficiently by an implementation which can use implementation-specific information and functionality). (5) Perhaps it would be worth adding the rule that it is an error to attempt to delete the current default directory; any attempt to do so should fail and cause Use_Error to be propagated. (6) I think the Form parameter should be added to the Rename and Copy_File procedures: procedure Rename (Old_Name, New_Name: in String; Form: in String := ""); procedure Copy_File (Source_Name, Target_Name: in String; Form: in String := ""); to allow implementation-specific information to be added (e.g. permissions). The advantage of this approach is that values for the Form parameter could be read from, say, a configuration file, thus making it possible to make a portable Ada program have useful implementation-specific behaviour. Because operations such as file renaming and copying are high-level operations anyway (likely to require the execution of many tens or hundreds of thousands of machine instructions), the overhead of interpreting Form strings is likely to be perfectly acceptable. (7) If passed a null string, the Containing_Directory function should return the parent of the current default directory (or the root directory if the current default directory is the root, or if the OS does not support hierarchical directories). A null string would then be a useful default value: function Containing_Directory (Name: in String := "") return String; (8) Maybe the functions Randy was referring to should be as follows. Remove the Compose function. Add: function [Compose_]Directory_Name ( Simple_Name: in String; Containing_Directory: in String := "" ) return String; which returns the full name of a directory which represents the path which starts at Containing_Directory (assumed to already be a valid path), and adds the directory named Simple_Name. Also add: function [Compose_]File_Name ( Simple_Name: in String; Containing_Directory: in String := "" ) return String; which returns the full name of a file which represents the path comprising Containing_Directory and Simple_Name. Finally, add: function [Compose_]Simple_Name ( Base_Name: in String; Extension: in String := "" ) return String; which returns the simple name comprising the given Base_Name and Extension. E.g. [Compose_]Simple_Name("myfile","txt") = "myfile.txt". I think the 'Compose_' prefixes are a bit long. Perhaps replacing them with 'To_' would be better. (9) Please change the name of the type Directory_Entry_Type to just Directory_Entry, and parameters named Directory_Entry to just Item. Admittedly this is merely cosmetic, but I do feel these names are wordier than they need to be. (10) File_Kind might be defined as follows: type File_Kind is ( Directory, -- a subdirectory Ordinary_File, -- any file (text or binary) Special_File, -- any non-file nevertheless openable Reserved ); -- anything (non-dir) not openable By 'openable' I mean able to be opened (in general) by the Open procedure of (at least) one of the *_IO packages (whichever is/are appropriate to the file or device). The idea behind the new category (Special_File) is to make provision for those entities which are openable, but which may not be renamable, copyable, or deletable (e.g. because they are not normal files). I think the name Special_File is more appropriate here, and a name such as Reserved is better for those entities which are 'something else'. (11) We could have: Everything: constant Filter_Type := (others => True); Openables: constant Filter_Type := (Ordinary_File | Special_File => True, others => False); (12) I would like to see the addition of some useful basic pattern helpers, e.g.: Match_All_Names: constant String; function Match_Filetype (Filetype: in String) return String; which would help enable code to be more portable. Match_All_Names might have the value "*" or "*.*", for example. (13) The Start_Search procedure could be declared: procedure Start_Search (Search: in out Search_Type; Filter: in Filter_Type := Everything; Pattern: in String := Match_All_Names; Directory: in String := ""); The default value of "" for the Directory parameter would specify the current default directory. (14) I think the standard should explicitly permit a directory to be locked by a call to Start_Search; if so it must be unlocked by a call to End_Search. Obviously most implmentations won't do this, but some may wish to. (15) Add: function Temporary_Directory return String; which returns the full name of a temporary directory, which should be readable, writable, and, preferably, exclusive to the calling program (process) and initially empty. Returns "" if not available; propagates Use_Error if not applicable. (16) Add: function Root_Directory (Directory: in String := "") return String; which returns the full name of a/the root directory (of the physical storage device on which the directory or directory entry Directory resides). The default "" for the Directory parameter indicates the current default directory. Propoagtes Use_Error if N/A. Examples of the result: "C:\", "/". (17) Introduce to Ada.Sequential_IO, Ada.Direct_IO, Ada.Text_IO, Ada.Wide_Text_IO, and Ada.Streams.Stream_IO: procedure Rename (File: in out File_Type; New_Name: in String; Mode: in File_Mode := In_File); which has the effect of resetting the File object (to the given Mode), and at the same time renaming it. This procedure would be very useful in facilitating a typical read-an-original-file, rewrite-it-to-a-temporary-file, rename-the-original-file, rename-the- temporary-file cycle. In an environment where there are multiple processes executing in parallel, and files are locked while open, this procedure could provide exactly the right locking semantics for the described cycle (providing the application program also behaves appropriately). It should also be mandated by the standard that this procedure is 'destructive': if an ordinary file corresponding to New_Name exists, it is automatically deleted and replaced. **************************************************************** From: Steve Deller Sent: Monday, January 21, 2002 1:48 PM > For the Get function, if an environment variable with the > requested Name exists, its value (also a string) is returned, > otherwise a null string is returned. POSIX environments distinguish "null" from "does not exist". This function needs a similar distinction, or perhaps have two functions, "get" and function Exists (Name : in [Wide_]String) return Boolean > The Environment_Name function is intended to return a broad > identification of the program's execution environment, e.g. > the basic name of an operating system or RTS. The > Environment_Version function is intended to give a more > precise identification of the same. Possible examples of the > results of Environment_Name and Environment_Version might be: > "Linux" and "2.4 (Red Hat 6.2)"; "Windows" and "NT 4.0"; > "BSD" and "4.4"; "OS/2" and "4.0". How does this differ from the "intent" of System.Name and System.System_Name? The rest of the comment seems bent on redefining "POSIX Ada" and adding it to the LRM. Is that true? Where is the proposed specification of "Ada.Directories"? ****************************************************************