This is an appendix to Understanding Unix/Linux Programming written for people interested in Ada programming on POSIX systems.

Directories and File Properties: Looking through ls

Section 3.4.3, page 76

In the package POSIX.Files we find operations on files and directories. This includes the generic procedure For_Every_Directory_Entry, which we can use to perform an action on every entry in a directory:

   generic
      with procedure Action (D_Entry : in     Directory_Entry;
                             Quit    : in out Boolean);
   procedure For_Every_Directory_Entry
      (Pathname : in     POSIX.Pathname);

If you read through the specification of package POSIX.Files, you can also find information on how to access the information stored in Directory_Entry objects.

Section 3.5, page 78

Here is a first draft of ls: ls1.adb

Compile it like this:

% gnatmake -P ls1

Run the program, and compare it with the system version of ls:

% ./ls1
.
..
C
index.html
Makefile
ls1.ali
ls1.adb
ls1.o
posix-strings.ali
posix-strings.o
.index.html.swp
filesize.adb
fileinfo.adb
ls2.adb
ls1
% ls
C         fileinfo.adb  index.html  ls1.adb  ls1.o    posix-strings.ali
Makefile  filesize.adb  ls1         ls1.ali  ls2.adb  posix-strings.o
%

Section 3.6.3, page 82

The following example shows how to use Get_File_Status to find the size of a file: filesize.adb

Compile it like this:

% gnatmake -P filesize

Run the program, and compare it with the output from wc -c:

% ./filesize
 The size of /etc/passwd is  1753
% wc -c /etc/passwd
1753 /etc/passwd
%

Section 3.6.4, page 83

The package specification for POSIX.File_Status shows us how we can extract the detailed information printed by ls -l from a Status object:

File types:
function Is_Block_Special_File (File_Status : Status) return Boolean;
function Is_Character_Special_File (File_Status : Status) return Boolean;
function Is_Directory (File_Status : Status) return Boolean;
function Is_FIFO (File_Status : Status) return Boolean;
function Is_Symbolic_Link (File_Status : Status) return Boolean;
function Is_Regular_File (File_Status : Status) return Boolean;
function Is_Socket (File_Status : Status) return Boolean;
function Is_Shared_Memory (File_Status : Status) return Boolean;
function Is_Message_Queue (File_Status : Status) return Boolean;
function Is_Semaphore (File_Status : Status) return Boolean;
Permissions:
function Permission_Set_Of (File_Status : Status) return POSIX.Permissions.Permission_Set;
Owner:
function Owner_Of (File_Status : Status) return POSIX.Process_Identification.User_ID;
Group:
function Group_Of (File_Status : Status) return POSIX.Process_Identification.Group_ID;
File size in bytes:
function Size_Of (File_Status : Status) return POSIX.IO_Count;
Number of links to file:
function Link_Count_Of (File_Status : Status) return Links;
Last content-modified time:
function Last_Modification_Time_Of (File_Status : Status) return POSIX.Calendar.POSIX_Time;

The type contains some other data, but these are the ones ls -l displays. The next sample program, fileinfo.adb, retrieves and prints those attributes.

Compile and run fileinfo, and then compare the output with that from ls -l:

% gnatmake -P fileinfo
[...]
% ./fileinfo fileinfo.adb
   mode: rw-r--r--
  links:  1
   user:  2001
  group: 2001
   size:  1257
modtime:  1169135465
   name: fileinfo.adb
% ls -l fileinfo.adb
-rw-r--r-- 1 sparre sparre 1257 2007-01-18 16:51 fileinfo.adb
%

Section 3.6.5, page 84

[...] Since the type POSIX.Permissions.Permission_Set is an array of Booleans, we need to convert it to something printable. We do that using POSIX.Permissions.Text_IO, which is a portable addition to the POSIX interface written for this book.

Section 3.6.6, page 88

The function POSIX.Permissions.Text_IO.Image converts a Permission_Set to a string by iterating through all the possible permissions, checking which ones are set.

If the file types were represented as an enumerated type, we could convert the file type to a character using a constant array like this:

To_Letter : constant array (File_Types) of Character :=
              (Directory              => 'd',
               Character_Special_File => 'c',
               Block_Special_File     => 'b',
               others                 => '-');

Since we are forced to check the file type one function call at a time, the code becomes much more complicated:

if Is_Directory (File_Status => Info) then
   return 'd';
elsif Is_Character_Special_File (File_Status => Info) then
   return 'c';
elsif Is_Block_Special_File (File_Status => Info) then
   return 'b';
else
   return '-';
end if;

Section 3.6.7, page 90

The package POSIX.User_Database allows us to look up information about users from their User_IDs. The function Get_User_Database_Item gives us a User_Database_Item from a user ID or name.

Once we have a User_Database_Item object, we can get the user name (function User_Name_Of), home directory (function Initial_Directory_Of), etc. from that.

Section 3.6.7, page 91

The package POSIX.Group_Database contains declarations similar to those in POSIX.User_Database, but for user groups.

Section 3.6.8, page 91

[...] The program ls2.adb is the result.

Compile and run ls2, and then compare the output with that from ls -l:

% gnatmake -P ls2
[...]
% ./ls2
drwxr-xr-x  2 sparre   sparre          - Jan 19 16:17 .
drwxr-xr-x 18 sparre   sparre          - Jan 19 15:50 ..
dr-xr-xr-x  2 sparre   sparre          - Jan 19 08:51 C
-rw-r--r--  1 sparre   sparre       8869 Jan 19 16:17 index.html
-rw-r--r--  1 sparre   sparre        900 Jan 18 17:42 Makefile
-rw-r--r--  1 sparre   users         870 Jan 17 16:45 ls1.adb
-rw-r--r--  1 sparre   users         296 Jan 17 17:12 filesize.adb
-rw-r--r--  1 sparre   users        1257 Jan 18 16:51 fileinfo.adb
-rw-r--r--  1 sparre   users        4166 Jan 19 16:15 ls2.adb
-rw-r--r--  1 sparre   sparre      24576 Jan 19 16:17 .index.html.swp
-rwxr-xr-x  1 sparre   sparre      46563 Jan 19 16:10 ls2
% ls -l
totalt 84
lrwxrwxrwx 1 sparre sparre    13 2007-01-18 13:08 C -> ../../UULP/03
-rw-r--r-- 1 sparre users   1257 2007-01-18 16:51 fileinfo.adb
-rw-r--r-- 1 sparre users    296 2007-01-17 17:12 filesize.adb
-rw-r--r-- 1 sparre sparre  8869 2007-01-19 16:17 index.html
-rw-r--r-- 1 sparre users    870 2007-01-17 16:45 ls1.adb
-rwxr-xr-x 1 sparre sparre 46563 2007-01-19 16:10 ls2
-rw-r--r-- 1 sparre users   4166 2007-01-19 16:15 ls2.adb
-rw-r--r-- 1 sparre sparre   900 2007-01-18 17:42 Makefile
% LANG=C ls -l
total 84
lrwxrwxrwx 1 sparre sparre    13 Jan 18 13:08 C -> ../../UULP/03
-rw-r--r-- 1 sparre sparre   900 Jan 18 17:42 Makefile
-rw-r--r-- 1 sparre users   1257 Jan 18 16:51 fileinfo.adb
-rw-r--r-- 1 sparre users    296 Jan 17 17:12 filesize.adb
-rw-r--r-- 1 sparre sparre  8869 Jan 19 16:17 index.html
-rw-r--r-- 1 sparre users    870 Jan 17 16:45 ls1.adb
-rwxr-xr-x 1 sparre sparre 46563 Jan 19 16:10 ls2
-rw-r--r-- 1 sparre users   4166 Jan 19 16:15 ls2.adb
%

How did We Do?

We are pretty close to the output format from ls -l.

Section 3.9.2, page 99

We use function POSIX.IO.Open_Or_Create to create new files.

We use procedure POSIX.Permissions.Set_Allowed_Process_Permissions to limit the allowed permissions for newly created files.

We use procedure POSIX.Files.Change_Permissions to change the access permissions for existing files.

Section 3.9.4, page 101

We use procedure POSIX.Files.Change_Owner_And_Group to change the user and group owning existing files.

Section 3.9.6, page 101-102

We use procedure POSIX.Files.Set_File_Times to change the modification and access times for existing files.

Section 3.9.7, page 102-103

We use procedure POSIX.Files.Rename to change the name of existing files.

Explorations, pages 104-105

3.1, Length of d_name[]

Just a hack for C programmers.

3.7, getdents(2)

Compare the Linux kernel function getdents with the Ada subroutine POSIX.Files.For_Every_Directory_Entry and the C subroutine readdir.

Programming exercises, pages 105-106

3.12

Unfortunately the POSIX/Ada interface on Linux is missing access to read and set the sticky bit, so you may have to limit yourselves to handling the set user id and set group id flags.

3.17, Locking the barn

If you turn off the read permission for a file, you cannot open the file for reading. What if you have already opened the file for reading, and then turn off the read permission from another terminal? Do subsequent calls to POSIX.IO.Read fail? Write a program that opens a file for reading, reads a few bytes, then uses delay 20.0; to wait for 20 seconds, then reads more. During that 20 seconds, make the file unreadable. What happens? Explain what read permission really means.

-- 

Go to next chapter.

Collected examples:
posix-in-ada.zip
Based on:
Understanding Unix/Linux Programming, Bruce Molay, ISBN 0-13-008396-8.
Main page:
http://edb.jacob-sparre.dk/Posix_in_Ada/
Written by:
Jacob Sparre Andersen.
Latest update:
19th of April, 2008