~strahinja/adash

3eca602f55aeafe8b44ff2d7d8c772704063974a — Страхиња Радић 2 months ago 40546f8
Reorganized source into several packages; updated .do files

Signed-off-by: Страхиња Радић <contact@strahinja.org>
9 files changed, 306 insertions(+), 159 deletions(-)

M adash.do
A adash_cmd.adb
A adash_cmd.ads
A adash_const.ads
A adash_parse.adb
A adash_parse.ads
M default.o.do
M main.adb
A main.o.do
M adash.do => adash.do +4 -5
@@ 1,9 1,8 @@
FILES=main
MAIN=main
FILES="main adash_cmd adash_const adash_parse"
FILES_ADB=$(echo $FILES | sed -e's/\(\<\S*\>\)/\1.adb/g')
FILES_O=$(echo $FILES | sed -e's/\(\<\S*\>\)/\1.o/g')
echo "$FILES_O" | xargs redo-ifchange 
for f in "$FILES"; do
	gnatbind $f
done
gnatlink $FILES -o $3
gnatbind $MAIN
gnatlink $MAIN -o $3


A adash_cmd.adb => adash_cmd.adb +113 -0
@@ 0,0 1,113 @@
-- See the file LICENSE for copyright and license details. --
with Ada.Characters.Latin_1, Ada.Exceptions, Ada.Text_IO,
	GNAT.Directory_Operations, Adash_Const;
use Ada.Characters.Latin_1, Ada.Exceptions, Ada.Text_IO,
	GNAT.Directory_Operations, Adash_Const;
package body Adash_Cmd is
	procedure Cat_File(filename: String) is
		input: File_Type;
		line: String(1..File_Buf_Max);
		last: Natural;
	begin
		Open(input, Mode => In_File, Name => filename);
		loop
			Get_Line(input, line, last);
			Put_Line(line(1..last));
			exit when End_Of_File(input);
		end loop;
		Close(input);
	exception
		when E: Name_Error =>
			Put_Line(Exception_Message(E));
	end Cat_File;

	procedure Change_Directory(dir: String) is
	begin
		Put_Line("Change_Directory(" & dir & ")");
		Change_Dir(dir);
	exception
		when E: Directory_Error =>
			Put_Line(Exception_Message(E));
	end Change_Directory;

	procedure Less_File(filename: String) is
		input: File_Type;
		line: String(1..File_Buf_Max);
		Line_Number: Natural;
		last: Natural;
		Continue_Input: String(1..Input_Buf_Max);
	begin
		Open(input, Mode => In_File, Name => filename);
		Line_Number := 1;
		loop
			Get_Line(input, line, last);
			Put_Line(line(1..last));
			Line_Number := Line_Number + 1;
			if Line_Number = Adash_Const.Page_Length then
				Put(": ");
				Get_Line(Continue_Input, last);
				if Continue_Input(1..last) = "q" then
					exit;
				end if;
				Line_Number := 1;
			end if;
			exit when End_Of_File(input);
		end loop;
		Close(input);
	exception
		when E: Name_Error =>
			Put_Line(Exception_Message(E));
	end Less_File;

	procedure List_Files(pattern: String := "") is
		dir: Dir_Type;
		dirname: String(1..Path_Max);
		Dir_Len: Natural;
		filename: String(1..Path_Max);
		last: Natural;
	begin
		Get_Current_Dir(dirname, Dir_Len);
		Put_Line("Directory " & dirname(1..Dir_Len));
		Open(dir, dirname(1..Dir_Len));
		loop
			Read(dir, filename, last);
			if last > 0 and last < Path_Max-1 then
				Put(filename(1..last) & " ");
			else
				Put_Line("");
			end if;
			exit when last = 0;
		end loop;
		Close(dir);
	end List_Files;

	procedure Show_Commands is
	begin
		Put_Line("Commands: cat cd copying exit help less ls version");
	end Show_Commands;

	procedure Show_Copying is
	begin
		Put_Line(
"adash - A shell written in Ada" & 
LF &
"Copyright (C) 2021 Strahinya Radich" & LF & LF &
"This program is free software: you can redistribute it and/or modify" & LF &
"it under the terms of the GNU General Public License as published by" & LF &
"the Free Software Foundation, either version 3 of the License, or" & LF &
"(at your option) any later version." & LF & LF &
"This program is distributed in the hope that it will be useful," & LF &
"but WITHOUT ANY WARRANTY; without even the implied warranty of" & LF &
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the" & LF &
"GNU General Public License for more details." & LF & LF &
"You should have received a copy of the GNU General Public License" & LF &
"along with this program.  If not, see <https://www.gnu.org/licenses/>.");
	end Show_Copying;

	procedure Show_Version is
	begin
		Put_Line("adash v" & Version);
	end Show_Version;

end Adash_Cmd;


A adash_cmd.ads => adash_cmd.ads +11 -0
@@ 0,0 1,11 @@
-- See the file LICENSE for copyright and license details. --
package Adash_Cmd is
	procedure Cat_File(filename: String);
	procedure Change_Directory(dir: String);
	procedure Less_File(filename: String);
	procedure List_Files(pattern: String := "");
	procedure Show_Commands;
	procedure Show_Copying;
	procedure Show_Version;
end Adash_Cmd;


A adash_const.ads => adash_const.ads +10 -0
@@ 0,0 1,10 @@
-- See the file LICENSE for copyright and license details. --
package Adash_Const is
	Path_Max:	constant Natural	:= 1024;
	File_Buf_Max:	constant Natural	:= 4096;
	Input_Buf_Max:	constant Natural	:= 1024;
	Prompt:		constant String		:= ") ";
	Version:	constant String		:= "1.0-alpha";
	Page_Length:	constant Natural	:= 24;
end Adash_Const;


A adash_parse.adb => adash_parse.adb +134 -0
@@ 0,0 1,134 @@
-- See the file LICENSE for copyright and license details. --
with Ada.Strings, Ada.Strings.Unbounded, Ada.Strings.Maps, Ada.Text_IO,
	Adash_Const, Ada.Characters.Latin_1, Adash_Cmd;
use Ada.Strings, Ada.Strings.Unbounded, Ada.Strings.Maps, Ada.Text_IO,
	Adash_Const, Adash_Cmd;
package body Adash_Parse is

	type Adash_Command is (
		Command_None,
		Command_Cat,
		Command_Cd,
		Command_Copying,
		Command_Exit,
		Command_Help,
		Command_Less,
		Command_Ls,
		Command_Version);

	Adash_Commands: constant Array(Adash_Command) of Unbounded_String := (
		To_Unbounded_String(""),
		To_Unbounded_String("cat"),
		To_Unbounded_String("cd"),
		To_Unbounded_String("copying"),
		To_Unbounded_String("exit"),
		To_Unbounded_String("help"),
		To_Unbounded_String("less"),
		To_Unbounded_String("ls"),
		To_Unbounded_String("version")
		);

	Unknown_Command_Exception: exception;

	function Starts_With(s: Unbounded_String; w: Unbounded_String)
		 return Boolean is
	begin
		if Length(s) < Length(w) then
			return FALSE;
		end if;
		return Slice(s, 1, Length(w)) = To_String(w);
	end Starts_With;

	procedure Parse_Input(input: Unbounded_String; running: out Boolean) is
		Index_Current    : Natural;
		Index_Start      : Natural;
		Index_End        : Natural;
		Token            : Unbounded_String;
		Command          : Adash_Command;
		Found_Command    : Boolean;
		Command_Executed : Boolean;
	begin
		Command          := Command_None;
		Command_Executed := FALSE;
		Found_Command    := FALSE;
		Index_Current    := 1;
		running          := TRUE;

		while Index_Current <= Length(input) loop
			Find_Token(
				Source => input,
				Set    => To_Set(" " & 
					Ada.Characters.Latin_1.HT),
				From   => Index_Current,
				Test   => Outside,
				First  => Index_Start,
				Last   => Index_End);
			exit when Index_End = 0;
			Token := To_Unbounded_String(To_String(
				input)(Index_Start..Index_End));
			if Found_Command and not Command_Executed then
				case Command is
					when Command_Cat =>
						Cat_File(To_String(Token));
						Command_Executed := TRUE;
					when Command_Cd =>
						Change_Directory(To_String(
							Token));
						Command_Executed := TRUE;
					when Command_Less =>
						Less_File(To_String(Token));
						Command_Executed := TRUE;
					when Command_Ls =>
						List_Files(To_String(Token));
						Command_Executed := TRUE;
					when others =>
						Put_Line(To_String(
							Adash_Commands(Command))
							& ": Extra parameters");
				end case;
			elsif not Command_Executed then
				Command := Adash_Command'First;
				while Command /= Adash_Command'Last loop
					exit when Adash_Commands(Command) 
						= Token; 
					Command := Adash_Command'Succ(Command);
				end loop;
				if Command = Adash_Command'Last and
					Adash_Commands(Command) /= Token then
					Put_Line("Unknown command: `" & 
						To_String(Token) & "'. Type "
						& "`help' for a list of " 
						& "commands.");
					return;
				end if;
				running := Command /= Command_Exit;
				Found_Command := TRUE;
				Token := To_Unbounded_String("");
			end if;
			Index_Current := Index_End + 1;
		end loop;
		if not Command_Executed then
			case Command is
				when Command_Cat =>
					Put_Line("Usage: cat [file]");
				when Command_Cd =>
					Put_Line("Usage: cd [directory]");
				when Command_Copying =>
					Show_Copying;
				when Command_Exit =>
					running := FALSE;
				when Command_Help =>
					Show_Commands;
				when Command_Less =>
					Put_Line("Usage: less [file]");
				when Command_Ls =>
					List_Files;
				when Command_Version =>
					Show_Version;
				when others => Null;
			end case;
		end if;
	end Parse_Input;

end Adash_Parse;


A adash_parse.ads => adash_parse.ads +12 -0
@@ 0,0 1,12 @@
-- See the file LICENSE for copyright and license details. --
with Ada.Strings.Unbounded;
use Ada.Strings.Unbounded;
package Adash_Parse is

	function Starts_With(s: Unbounded_String; 
		w: Unbounded_String) return Boolean;

	procedure Parse_Input(input: Unbounded_String; running: out Boolean);

end Adash_Parse;


M default.o.do => default.o.do +8 -1
@@ 1,4 1,11 @@
if [ -f $2.adb ]; then
	SOURCE=$2.adb
else
	SOURCE=$2.ads
fi

redo-ifchange $SOURCE
trap "rm -f $2.o" HUP PIPE INT QUIT TERM EXIT
gnatgcc -c -O2 $2.adb -gnata -o $2.o
gnatgcc -c -O2 $SOURCE -gnata -o $2.o
mv $2.o $3


M main.adb => main.adb +4 -153
@@ 1,167 1,18 @@
-- See the file LICENSE for copyright and license details. --
with Ada.Text_IO, Ada.Strings.Unbounded,
	Ada.Characters.Latin_1, GNAT.Directory_Operations, Ada.Exceptions,
	Ada.IO_Exceptions, Ada.Strings;
use Ada.Text_IO, Ada.Characters.Latin_1, GNAT.Directory_Operations,
	Ada.Exceptions, Ada.Strings.Unbounded, GNAT.Directory_Operations;
with Ada.IO_Exceptions, Ada.Text_IO, Ada.Strings.Unbounded, Adash_Const,
	Adash_Parse;
use Ada.Text_IO, Ada.Strings.Unbounded, Adash_Const, Adash_Parse;
procedure Main is

	Path_Max:	constant Natural	:= 1024;
	File_Buf_Max:	constant Natural	:= 4096;
	Input_Buf_Max:	constant Natural	:= 1024;
	Prompt:		constant String		:= ") ";
	Version:	constant String		:= "1.0-alpha";
	Page_Length:	constant Natural	:= 24;

	buf:		Unbounded_String;
	running:	Boolean;

	function Starts_With(s: Unbounded_String; w: Unbounded_String)
		 return Boolean is
	begin
		if Length(s) < Length(w) then
			return FALSE;
		end if;
		return Slice(s, 1, Length(w)) = To_String(w);
	end Starts_With;

	procedure Show_Commands is
	begin
		Put_Line("Commands: cat cd copying exit help less ls version");
	end Show_Commands;

	procedure Show_Version is
	begin
		Put_Line("adash v" & Version);
	end Show_Version;

	procedure Cat_File(filename: String) is
		input: File_Type;
		line: String(1..File_Buf_Max);
		last: Natural;
	begin
		Open(input, Mode => In_File, Name => filename);
		loop
			Get_Line(input, line, last);
			Put_Line(line(1..last));
			exit when End_Of_File(input);
		end loop;
		Close(input);
	exception
		when E: Name_Error =>
			Put_Line(Exception_Message(E));
	end Cat_File;

	procedure Change_Directory(dir: String) is
	begin
		Put_Line("Change_Directory(" & dir & ")");
		Change_Dir(dir);
	exception
		when E: Directory_Error =>
			Put_Line(Exception_Message(E));
	end Change_Directory;

	procedure Show_Copying is
	begin
		Put_Line(
"adash - A shell written in Ada" & 
LF &
"Copyright (C) 2021 Strahinya Radich" & LF & LF &
"This program is free software: you can redistribute it and/or modify" & LF &
"it under the terms of the GNU General Public License as published by" & LF &
"the Free Software Foundation, either version 3 of the License, or" & LF &
"(at your option) any later version." & LF & LF &
"This program is distributed in the hope that it will be useful," & LF &
"but WITHOUT ANY WARRANTY; without even the implied warranty of" & LF &
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the" & LF &
"GNU General Public License for more details." & LF & LF &
"You should have received a copy of the GNU General Public License" & LF &
"along with this program.  If not, see <https://www.gnu.org/licenses/>.");
	end Show_Copying;

	procedure Less_File(filename: String) is
		input: File_Type;
		line: String(1..File_Buf_Max);
		Line_Number: Natural;
		last: Natural;
		Continue_Input: String(1..Input_Buf_Max);
	begin
		Open(input, Mode => In_File, Name => filename);
		Line_Number := 1;
		loop
			Get_Line(input, line, last);
			Put_Line(line(1..last));
			Line_Number := Line_Number + 1;
			if Line_Number = Page_Length then
				Put(": ");
				Get_Line(Continue_Input, last);
				if Continue_Input(1..last) = "q" then
					exit;
				end if;
				Line_Number := 1;
			end if;
			exit when End_Of_File(input);
		end loop;
		Close(input);
	exception
		when E: Name_Error =>
			Put_Line(Exception_Message(E));
	end Less_File;

	procedure List_Files is
		dir: Dir_Type;
		dirname: String(1..Path_Max);
		Dir_Len: Natural;
		filename: String(1..Path_Max);
		last: Natural;
	begin
		Get_Current_Dir(dirname, Dir_Len);
		Put_Line("Directory " & dirname(1..Dir_Len));
		Open(dir, dirname(1..Dir_Len));
		loop
			Read(dir, filename, last);
			if last > 0 and last < Path_Max-1 then
				Put(filename(1..last) & " ");
			else
				Put_Line("");
			end if;
			exit when last = 0;
		end loop;
		Close(dir);
	end List_Files;
begin
	running := TRUE;
Main_Loop:
	loop
		Put(Prompt);
		buf := To_Unbounded_String(Get_Line);
		if Starts_With(buf, To_Unbounded_String("cat ")) then
			Cat_File(Slice(buf, 5, Length(buf)));
		elsif Starts_With(buf, To_Unbounded_String("cd ")) then
			Change_Directory(Slice(buf, 4, Length(buf)));
		elsif Starts_With(buf, To_Unbounded_String("less ")) then
			Less_File(Slice(buf, 6, Length(buf)));
		elsif To_String(buf) = "cat" then
			Put_Line("Usage: cat [file]");
		elsif To_String(buf) = "cd" then
			Put_Line("Usage: cd [directory]");
		elsif To_String(buf) = "copying" then
			Show_Copying;
		elsif To_String(buf) = "exit" then
			running := FALSE;
		elsif To_String(buf) = "help" then
			Show_Commands;
		elsif To_String(buf) = "less" then
			Put_Line("Usage: less [file]");
		elsif To_String(buf) = "version" then
			Show_Version;
		elsif To_String(buf) = "ls" then
			List_Files;
		else
			Put_Line("Unknown command `"
				& To_String(buf) & "'. Type `help' for a "
				& "list of commands.");
		end if;
		Parse_Input(To_Unbounded_String(Get_Line), running);
		exit Main_Loop when not running;
	end loop Main_Loop;
	exception

A main.o.do => main.o.do +10 -0
@@ 0,0 1,10 @@
MAIN=main
MAIN_O=main.o
MAIN_ADB=main.adb
FILES="adash_cmd adash_const adash_parse"
FILES_O=$(echo $FILES | sed -e's/\(\<\S*\>\)/\1.o/g')
echo "$FILES_O" | xargs redo-ifchange
trap "rm -f $MAIN_O" HUP PIPE INT QUIT TERM EXIT
gnatgcc -c -O2 $MAIN_ADB -gnata -o $MAIN_O
mv $MAIN_O $3