getada_1.0.1_e3e93ee6/src/shells.adb

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
--    Copyright (C) 2022-2024 A.J. Ianozi <aj@ianozi.com>
--
--    This file is part of GetAda: the Unofficial Alire Installer
--
--    This program is free software: you can redistribute it and/or modify
--    it under the terms of the GNU General Public License as published by
--    the Free Software Foundation, either version 3 of the License, or
--    (at your option) any later version.
--
--    This program is distributed in the hope that it will be useful,
--    but WITHOUT ANY WARRANTY; without even the implied warranty of
--    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--    GNU General Public License for more details.
--
--    You should have received a copy of the GNU General Public License
--    along with this program.  If not, see <https://www.gnu.org/licenses/>.

with Ada.Strings.Fixed;       use Ada.Strings.Fixed;
with Ada.Characters.Handling; use Ada.Characters.Handling;
with Platform;                use Platform;
with Ada.Environment_Variables;
with Ada.Directories;

package body Shells is
   --  Get the resulting configuration file to edit, based on the shell.
   function Get_Shell_Config (Shell_Name : Supported_Shells) return String is
   begin
      case Shell_Name is
         when sh | bash =>
            return ".profile";
         when zsh =>
            return ".zshenv";
         when others =>
            raise Unsupported_Shell with Shell_Name'Image & " not supported";
      end case;
   end Get_Shell_Config;
   --  I'm doing it this way in case I want to add support for fish, e.g.
   --  "env.fish" etc.
   function Get_Shell_Env (Shell_Name : Supported_Shells) return String is
   begin
      case Shell_Name is
         when sh | bash | zsh =>
            return "env.sh";
         when others =>
            raise Unsupported_Shell with Shell_Name'Image & " not supported";
      end case;
   end Get_Shell_Env;
   procedure Write_Env_File
     (Shell_Name : Supported_Shells; File : File_Type; Dir : String)
   is
   begin
      case Shell_Name is
         when sh | bash | zsh =>
            Put_Line (File, "#!/bin/sh");
            Put_Line (File, "# Add alire's dir to path");
            Put_Line (File, "case "":$PATH:"" in");
            Put_Line (File, "*"":" & Dir & ":""*)");
            Put_Line (File, "  : ;; # already exists, do nothing.");
            Put_Line (File, "* )");
            Put_Line (File, "  # Add to path");
            Put_Line (File, "  export PATH=""$PATH:" & Dir & """");
            Put_Line (File, "esac");
         when others =>
            raise Unsupported_Shell with Shell_Name'Image & " not supported";
      end case;
   end Write_Env_File;
   function Get_Env_Command
     (Shell_Name : Supported_Shells; Env_File : String) return String
   is
   begin
      case Shell_Name is
         when sh | bash | zsh =>
            return ". """ & Env_File & """";
         when null_shell =>
            raise Unsupported_Shell with Shell_Name'Image & " not supported";
      end case;
   end Get_Env_Command;
   --  Checks if this shell is compatible with the bourne shell
   function Sh_Compatible (Shell_Name : Supported_Shells) return Boolean is
   begin
      case Shell_Name is
         when sh | bash =>
            return True;
         when others =>
            return False;
      end case;
   end Sh_Compatible;

   function Available_Shells return Shell_Array
   is
      --  Env variable for current Shell
      Shell_Env : constant String := "SHELL";

      --  General location of available shells.
      Shell_Path : constant String := "/etc/shells";

      --  Truth table of avalaible shells
      Shell_Amount : Natural := 0; -- Amount of shells discovered
      Shell_List   : array (Supported_Shells'Range) of Boolean :=
        (others => False);

      --  This processes each shell using the established variables above
      procedure Process_Shell (Shell_Str : String) is
         Prefix : constant String := "/bin/"; -- All lines start with this?
      begin
         --  If this line is /bin/something
         if Shell_Str'Length > Prefix'Length
           and then Index (Shell_Str, Prefix) = 1
         then
            declare
               Check_Shell : constant String :=
                 To_Upper
                   (Shell_Str
                      (Shell_Str'First - 1 + Prefix'Length + 1 ..
                           Shell_Str'Last));
            begin
               for S in Shell_List'Range loop
                  if not Shell_List (S) --  the current item is false
                    and then S'Image = Check_Shell
                  then
                     if not Sh_Compatible (S) then
                        Shell_List (S) := True;
                        Shell_Amount   := Shell_Amount + 1;
                     elsif not Shell_List (sh) then
                        Shell_List (sh) := True;
                        Shell_Amount    := Shell_Amount + 1;
                     end if;
                  end if;
               end loop;
            end;
         end if;
      end Process_Shell;
   begin
      --  TODO: a better way to do this.
      if OS = Windows then
         declare
            Result : constant Shell_Array (1 .. 1) :=
              (1 => (Null_Unbounded_String, null_shell));
         begin
            return Result;
         end;
      end if;
      --  First log current shell.
      if Ada.Environment_Variables.Exists (Shell_Env) then
         Process_Shell (Ada.Environment_Variables.Value (Shell_Env));
      end if;
      --  Next, check the available shells
      if Ada.Directories.Exists (Shell_Path) then
         declare
            Shell_File : File_Type; -- To store the file
         begin
            Open (Shell_File, In_File, Shell_Path);
            --  Iterate through the file, indexing which shells we support.
            while not End_Of_File (Shell_File) loop
               declare
                  Next_Line : constant String := Get_Line (Shell_File);
               begin
                  Process_Shell (Next_Line);
               end;
            end loop;
            Close (Shell_File);
         end;
      end if;
      --  Verify that we have at least 1 found shell.
      if Shell_Amount = 0 then
         raise No_Shells_Found
           with "Cannot locate /etc/shells or $SHELL does not exist." &
           " Please pass ""--no-path""";
      end if;
      --  Create the array, fill it with resulting shells, return the result.
      declare
         Result  : Shell_Array (1 .. Shell_Amount);
         Counter : Positive := 1;
      begin
         for S in Shell_List'Range loop
            if Shell_List (S) then
               Result (Counter).Config_File :=
                 To_Unbounded_String (Get_Shell_Config (S));
               Result (Counter).Shell := S;
               Counter                := Counter + 1;
            end if;
         end loop;
         return Result;
      end;
   end Available_Shells;
end Shells;