This is an appendix to Understanding Unix/Linux
Programming
written for people interested in Ada programming on POSIX
systems.
lsIn 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.
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 %
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 %
The package specification for POSIX.File_Status shows us
how we can extract the detailed information printed by ls -l
from a Status object:
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;function Permission_Set_Of (File_Status :
Status) return
POSIX.Permissions.Permission_Set;function Owner_Of (File_Status : Status)
return POSIX.Process_Identification.User_ID;function Group_Of (File_Status : Status)
return
POSIX.Process_Identification.Group_ID;function Size_Of (File_Status : Status)
return POSIX.IO_Count;function Link_Count_Of (File_Status : Status)
return Links;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 %
[...] 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.
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;
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.
The package POSIX.Group_Database contains declarations
similar to those in POSIX.User_Database, but for user
groups.
[...] 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 %
We are pretty close to the output format from ls -l.
C) rather than showing it as a symbolic link.
..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.
We use procedure
POSIX.Files.Change_Owner_And_Group to change the user and group
owning existing files.
We use procedure
POSIX.Files.Set_File_Times to change the modification and access
times for existing files.
We use procedure POSIX.Files.Rename to
change the name of existing files.
Just a hack for C programmers.
getdents(2)Compare the Linux kernel function getdents with the Ada
subroutine POSIX.Files.For_Every_Directory_Entry and the C
subroutine readdir.
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.
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.
Understanding Unix/Linux Programming, Bruce Molay,
ISBN 0-13-008396-8.