As part of a little project, I'm writing a shell in Ada. As such, when I was investigating the system calls, I learned that there are three ways to do it.
- The POSIX system calls, which are probably the least reliable.
- Passing the arguments along to C's system(), which I didn't really want to do, since this was about writing the emulator in Ada and not C.
- Using GNAT's runtime libraries.
I chose to go for the last option, considering this to be the most "Ada-like" of the choices. I found a code snippet on RosettaCode here. I copied and pasted it and compiled it after changing the "cmd.exe" to "ls" and removing the second argument definition. However, nothing happens when I run the executable. The shell just goes right back to the prompt. I have tested this on two different computers, one running Fedora 21, the other Debian Jessie. Here's what I've done to test it:
- Seen if lacking an arguments string caused it
- Checked if any of the file descriptors in GNAT's libraries are mis-named
- Redirected both stderr and stdin to stdout just to see if GNAT was dumping them to the wrong FD anyway.
- Looked thoroughly through the System.OS_lib library file, and there seems to be no reason.
- Googled it, but GNAT's own page on the GCC website is very poorly documented.
For now I'm using the C.Interface system in the preparation of my shell, but I'm dissatisfied with this. I'm new to Ada and have only been tinkering with it for a month or so now, so if there's some kind of Ada wisdom here that would help I'm not in on it.
UPDATE: I have tried running it with absolute path, both to /usr/bin and /bin locations, and it doesn't work. Interestingly, the result code returned by the operating system is 1, but I don't know what that means. A quick search suggests that it's for "all general errors", and another site suggests that it's for "incorrect functions".
I had to tweak the RosettaCode example a little to run /bin/ls
on Debian Linux, but it does run as expected...
with Ada.Text_IO; use Ada.Text_IO;
with Gnat.OS_Lib; use Gnat.OS_Lib;
procedure Execute_Synchronously is
Result : Integer;
Arguments : Argument_List :=
( 1=> new String'("-al")
);
begin
Spawn
( Program_Name => "/bin/ls",
Args => Arguments,
Output_File_Descriptor => Standout,
Return_Code => Result
);
for Index in Arguments'Range loop
Free (Arguments (Index));
end loop;
end Execute_Synchronously;
Changes :
- my Gnat (FSF Gnat 4.92 from Debian Jessie) warned about
System.OS_Lib
, recommending Gnat.OS_Lib
instead. (Which simply renames System.OS_Lib .... why???
System.OS_Lib comments:
-- Note: this package is in the System hierarchy so that it can be directly
-- be used by other predefined packages. User access to this package is via
-- a renaming of this package in GNAT.OS_Lib (file g-os_lib.ads).
- Program name including path.
- Arguments. The first time I ran it, it displayed the details of "ls" itself, because it was given its own name as the first argument, so I deleted that to see the current directory instead.
Notes :
- the best information ot the available subprograms and their arguments is usually in the package specs themselves in the "adainclude" folder : this is
/usr/lib/gcc/x86_64-linux-gnu/4.9/adainclude
on my Debian installation, locate system.ads
will find yours. The specific files are: s-os_lib.ads
for System.OS_Lib
which exports Spawn and Standout, and a-textio.ads
for Ada.Text_IO.
Standout
is not the preferred way of accessing Standard Output : it's a file descriptor (integer), the preferred way would be the Standard_Output
function from Ada.Text_IO
which returns a File. However there doesn't seem to be an overload for Spawn
which takes a File (nor would I expect one in this low level library) so the lower level file descriptor is used here.
Absent a shell, you'll need to search the PATH
yourself or specify a full path for the desired executable:
Spawn (
Program_Name => "/bin/ls",
…
);
I have tried running it with absolute path…neither /usr/bin
nor /bin
locations work.
Use which
to determine the full path to the executable:
$ which ls
/bin/ls