<--Last Chapter | Table of Contents | Next Chapter--> |
Fun Fact:The original Apple Macintosh operating system was written in Pascal, the ancestor language of Ada. |
Ada
|
Description
|
C Equivalent
|
create_file
|
Create a Linux file
|
creat
|
delete_file
|
Delete a Linux file
|
unlink
|
etc.
|
The gnat OS library, gnat.os_lib, provides common UNIX
operations independent of what flavour of UNIX gnat is running on.
It provides an extensive set of file utilities as well as the
ability to run blocked and non-blocked child processes.The price
for this low-level OS support is the need to use a lot of
addresses, 'access and C strings.
![]() |
with text_io, gnat.os_lib;
-- for write test
FirstLine : constant string := "This is the first line in the file";
WasDeleted : boolean;
-- for spawn test
This is an example of the Gnat's OS library:
Creating a new file...
Locating the file we just made...
STARTUP
When installing binding libraries:
For example, to catch the SIGTERM signal, the signal that indicates that the program has been killed with the "kill" shell command, you can write a handler like this:
protectedbodySignalHandler is
procedure HandleSIGTERM is
To put the handler in place permanently, use pragma Attach_Handler.
pragma Attach_Handler( HandleSIGTERM, SIGTERM );
Now whenever your program receives a SIGTERM signal, your handler will automatically run.
If you don't want to install a permanent handler, a handler can be installed or changed while the program is running. To indicate that a procedure is an interrupt handler that can be installed at a later time, use pragma Interrupt_Handler.
pragma Interrupt_Handler( HandleSIGTERM );
Gnat automatically handles one signal for you: SIGINT, the interrupt signal. This is the signal that is sent to your program when control-c is pressed. If you want to handle control-c presses yourself, you have to use pragma Unreserve_All_Interrupts. Despite it's long name, this pragma simply tells Gnat to ignore SIGINT's.
Certain signals can never be caught. SIGUNUSED, the unused signal, can't be caught for obvious reasons. Some signals are used by the multithreading software and are not available for use in applications. In particular, if you are running native Linux threads, you can't catch SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGTRAP, SIGABRT, SIGINT, SIGVTALRM, SIGUNUSED, SIGSTOP, or SIGKILL. On 2.0 kernels or older, native Linux threads use SIGUSR1 and SIGSUR2 and are not available. If you're running FSU threads, then SIGALRM is also not available.
Ada.Interrupts also contains several subprograms for
signal handling.
with Ada.Interrupts.Names;
-- Package to handle basic Linux signals
pragma Unreserve_All_Interrupts;
procedure HandleControlC;
end SigHand;
with Ada.Text_IO;
-- Package to handle basic Linux signals
protected body SignalHandler is
end HandleControlC;
procedure HandleKill is
end HandlePowerFailure;
end SignalHandler;
end SigHand;
with Ada.Text_IO, SigHand, Ada.Interrupts.Names;
if Is_Reserved( SIGTERM ) then
if Is_Attached( SIGINT ) then
Put_Line( "Testing SIGTERM handler..." );
-- a long loop
New_Line;
Ada
|
Description
|
C Equivalent
|
Function Command_Name return string;
|
The name of this command (path?).
|
argv[0]
|
Function ArgumentCount return natural;
|
The number of arguments.
|
argn
|
Function Argument( n : natural ) return
string;
|
The n'th argument.
|
argv[n]
|
Procedure Set_Exit_Status( e : Exit_Status
);
|
The exit status to return.
|
exit( e )
|
Argument returns an argument. In the above example, argument( 1 ) returns "-v".
Set_Exit_Status gives Ada the error code you want to return when the program is finished running. Ada defines two Exit_Status values, Success and Failure . Since Exit_Status is just an integer, you can return other values. Zero indicates that the program ran without error, non-zero values indicate an error. The predefined values of Success and Failure are 0 and 1.
Properly flagging errors is important for shell programming. For example, you have to return the proper exit status for "myprog && echo 'all is well'" to work properly. You can retrieve the exit status of the last command using "$?". For example:
#!/bin/bash
Ada
|
Description
|
C Equivalent
|
Function Environment_Count return natural;
|
The number of environment variables
|
? |
Function Environment_Value(n) return string;
|
The name value of the nth variable
|
getenv(n)
|
The Environment_Count function returns the number of environment variables.
The Environment_Value function returns the name and value of a variable, separated by an equals sign. For example, Environment_Value( 5 ) returns the name and value of the fifth environment variable.
The following program is an example of Ada.Command_Line and Ada.Command_Line.Environment. The results assume that you started the program by typing "cmdtest -v".
with text_io, Ada.Command_Line.Environment;
begin
The command to invoke this example was 'cmdtest'
There is/are 24 environment variables.
Environment variables can be removed using the Gnat Ada.Command_Line.Remove package.
For this package, a directory name string (Dir_Name_Str) is a pathname in the standard Linux format. The trailing '/' character is optional when using this package, but directory names returned will always have a trailing '/'. "." is the current directory. ".." is the parent directory of the current directory.
Get_Current_Dir returns the name of the current directory. Change_Dir changes the current directory to a new location.
with ada.text_io, gnat.directory_operations; use ada.text_io, gnat.directory_operations; procedure gdir is dir : string(1..80); len : natural; begin Put( "The current working directory is " ); Put_Line( Get_Current_Dir ); Change_Dir( ".." ); Put( "Moving up, the current working directory is " ); Put_Line( Get_Current_Dir ); Change_Dir( "work" ); Get_Current_Dir( dir, len ); Put( "Moving down to 'work', the current working directory is " ); Put_Line( dir(1..len) ); end dir;
The current working directory is /home/kburtch/work/ Moving up, the current working directory is /home/kburtch/ Moving down to 'work', the current working directory is /home/kburtch/work/
For viewing directories, the package opens directories like a Text_IO file. Dir_Type is a limited private directory type corresponds to a file_type in Text_IO. Directories can only be read.
Any error will raise a DIRECTORY_ERROR exception
with ada.text_io, gnat.directory_operations; use ada.text_io, gnat.directory_operations; procedure gdir2 is dir : Dir_Type; dirname : string( 1..80 ); len : natural; begin if Read_Is_Thread_Safe then put_line( "Tasks may read the same directory" ); else put_line( "Tasks may not read the same directory" ); end if; New_Line; Open( dir, "." ); if Is_Open( dir ) then put_Line( "The directory was opened" ); else put_Line( "The directory was not opened" ); end if; loop Read( dir, dirname, len ); exit when len = 0; Put_Line( dirname( 1..len ) ); end loop; Put_Line( "End of directory" ); Close( dir ); end gdir2;
Tasks may not read the same directory The directory was opened . .. gdir.ads gdir.ali gdir.adb gdir.o gdir gdir2.adb gdir2.ali gdir2.o gdir2 End of directory
The directories "." and ".." are always returned.
New directories can be made with Make_Dir.
Make_Dir( "logs" ); -- make a new "logs" directory
If you need more features that these, the Linux kernel calls for directories are described in 16.9. The section includes a command to remove directories which cannot be done with Gnat.Directory_Operations.
Locks are implemented using lock files. When a file is locked, Gnat checks for the presence of a separate file. If the file exists, the file has been locked by another application. If a file cannot be locked, a LOCK_ERROR is raised.
The programmer supplies the lock file name. Linux programs usually place lock files in the /var/lock/ directory.
The Lock_File procedure locks a particular file. By default, if the procedure will continue trying to relock the file every second forever (actually, for Natural'Last seconds, a very long time). The delay and the number of retries can be changed.
Lock_File( "/var/lock/", "customers.txt" ); Lock_File( "/var/lock/customers.txt" ); Lock_File( "/var/lock/customers.txt", Wait => 5.0, Retries => 10 );
Files are unlocked using Unlock_File. This procedure deletes the lock file.
Unlock_File( LockDir, "customers.txt" ); Unlock_File( "/var/lock/customers.txt" );
The lock file approach is a voluntary convention. Programs that honour the convention can share the file in an orderly way. A program that doesn't use the package will not be denied access. For true file locking, use the Linux kernel calls described in 16.7.
Incomplete
Note: The Gnat socket package is very simple and reportedly returns an error if a socket is blocked. If you need to connect to blockable sockets, you'll need to write your own O/S socket bindings. Simple Server with GNAT.Sockets. Since this uses Ada streams, you'll only be able to connect to this server with an Ada client using Ada streams.
with Ada.Text_IO; with GNAT.Sockets; use GNAT.Sockets; use Ada.Text_IO; procedure server is server_host : constant string := "localhost"; server_port : constant Port_Type := 5876; server_address : Sock_Addr_type; server_socket : Socket_Type; client_socket : Socket_Type; client_stream : Stream_Access; begin server_address.addr := Addresses( Get_Host_By_Name( server_host ), 1 ); server_address.port := server_port; Create_Socket( server_socket ); Set_Socket_Option( server_socket, socket_level, (reuse_address, true ) ); begin put_line( "Binding..." ); Bind_Socket( server_socket, server_address ); exception when Socket_Error => put_line( standard_error, "Bind_Socket raised Socket_Error" ); end; begin put_line( "Listening..." ); Listen_Socket( server_socket ); exception when Socket_Error => put_line( standard_error, "Listen_Socket raised Socket_Error" ); end; begin put_line( "Accepting..." ); Accept_Socket( server_socket, client_socket, server_address ); exception when Socket_Error => put_line( standard_error, "Accept_Socket raised Socket_Error" ); end; client_stream := Stream( client_socket ); put_line( "Streamed..." ); put_line( "Blocking..." ); delay 0.2; put_line( "Reading channel.." ); declare message : string := string'input( client_stream ); begin put_line( "Message:" ); put_line( "Message Addr: " & Image( Get_Address( client_stream ) ) ); put_line( "Message Text: '" & message & "'" ); put_line( "Message Len: " & message'length'img ); exception when END_ERROR => put_line( "Premature end of data error" ); end; Close_Socket( server_socket ); Close_Socket( client_socket ); end server;
<--Last Chapter | Table of Contents | Next Chapter--> |