awa_2.4.0_59135a52/ada-keystore/tools/akt-commands-edit.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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
-----------------------------------------------------------------------
--  akt-commands-edit -- Edit content in keystore
--  Copyright (C) 2019, 2021, 2022 Stephane Carrez
--  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------
with Ada.Directories;
with Ada.Environment_Variables;
with Ada.Command_Line;
with Interfaces.C.Strings;
with Util.Files;
with Util.Processes;
with Util.Systems.Os;
with Util.Systems.Types;
with Util.Streams.Raw;
with Keystore.Random;
package body AKT.Commands.Edit is

   use GNAT.Strings;

   procedure Export_Value (Context : in out Context_Type;
                           Name    : in String;
                           Path    : in String);

   procedure Import_Value (Context : in out Context_Type;
                           Name    : in String;
                           Path    : in String);

   procedure Make_Directory (Path : in String);

   --  ------------------------------
   --  Export the named value from the wallet to the external file.
   --  The file is created and given read-write access to the current user only.
   --  ------------------------------
   procedure Export_Value (Context : in out Context_Type;
                           Name    : in String;
                           Path    : in String) is
      use Util.Systems.Os;
      use type Interfaces.C.int;
      use type Util.Systems.Types.File_Type;

      Fd   : Util.Systems.Os.File_Type;
      File : Util.Streams.Raw.Raw_Stream;
      P    : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (Path);
   begin
      Fd := Util.Systems.Os.Sys_Open (Path  => P,
                                      Flags => O_CREAT + O_WRONLY + O_TRUNC,
                                      Mode  => 8#600#);
      Interfaces.C.Strings.Free (P);
      if Fd < 0 then
         AKT.Commands.Log.Error (-("cannot create file for the editor"));
         raise Error;
      end if;
      File.Initialize (Fd);
      if Context.Wallet.Contains (Name) then
         Context.Wallet.Get (Name, File);
      end if;
   end Export_Value;

   procedure Import_Value (Context : in out Context_Type;
                           Name    : in String;
                           Path    : in String) is
      use Util.Systems.Os;
      use type Util.Systems.Types.File_Type;

      Fd   : Util.Systems.Os.File_Type;
      File : Util.Streams.Raw.Raw_Stream;
      P    : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (Path);
   begin
      Fd := Util.Systems.Os.Sys_Open (Path  => P,
                                      Flags => O_RDONLY,
                                      Mode  => 0);
      Interfaces.C.Strings.Free (P);
      if Fd < 0 then
         AKT.Commands.Log.Error (-("cannot read the editor's output"));
         raise Error;
      end if;
      File.Initialize (Fd);
      Context.Wallet.Set (Name, Keystore.T_STRING, File);
   end Import_Value;

   --  ------------------------------
   --  Get the editor command to launch.
   --  ------------------------------
   function Get_Editor (Command : in Command_Type) return String is
   begin
      if Command.Editor /= null and then Command.Editor'Length > 0 then
         return Command.Editor.all;
      end if;

      --  Use the $EDITOR if the environment variable defines it.
      if Ada.Environment_Variables.Exists ("EDITOR") then
         return Ada.Environment_Variables.Value ("EDITOR");
      end if;

      --  Use the editor which links to the default system-wide editor
      --  that can be configured on Ubuntu through /etc/alternatives.
      return "editor";
   end Get_Editor;

   --  ------------------------------
   --  Get the directory where the editor's file can be created.
   --  ------------------------------
   function Get_Directory (Command : in Command_Type;
                           Context : in out Context_Type) return String is
      pragma Unreferenced (Command, Context);

      Rand : Keystore.Random.Generator;
      Name : constant String := "akt-" & Rand.Generate (Bits => 32);
   begin
      return "/tmp/" & Name;
   end Get_Directory;

   procedure Make_Directory (Path : in String) is
      P      : Interfaces.C.Strings.chars_ptr;
      Result : Integer;
   begin
      Ada.Directories.Create_Path (Path);
      P := Interfaces.C.Strings.New_String (Path);
      Result := Util.Systems.Os.Sys_Chmod (P, 8#0700#);
      Interfaces.C.Strings.Free (P);
      if Result /= 0 then
         AKT.Commands.Log.Error (-("cannot set the permission of {0}"), Path);
         raise Error;
      end if;
   end Make_Directory;

   --  ------------------------------
   --  Edit a value from the keystore by using an external editor.
   --  ------------------------------
   overriding
   procedure Execute (Command   : in out Command_Type;
                      Name      : in String;
                      Args      : in Argument_List'Class;
                      Context   : in out Context_Type) is
   begin
      Context.Open_Keystore (Args);
      if Args.Get_Count /= Context.First_Arg then
         AKT.Commands.Usage (Args, Context, Name);

      else
         declare
            Dir    : constant String := Command.Get_Directory (Context);
            Path   : constant String := Util.Files.Compose (Dir, "VALUE.txt");
            Editor : constant String := Command.Get_Editor;
            Proc   : Util.Processes.Process;

            procedure Cleanup;

            procedure Cleanup is
            begin
               if Ada.Directories.Exists (Path) then
                  Ada.Directories.Delete_File (Path);
               end if;
               if Ada.Directories.Exists (Dir) then
                  Ada.Directories.Delete_Tree (Dir);
               end if;
            end Cleanup;

         begin
            Make_Directory (Dir);
            Export_Value (Context, Args.Get_Argument (Context.First_Arg), Path);
            Util.Processes.Spawn (Proc, Editor & " " & Path);
            Util.Processes.Wait (Proc);
            if Util.Processes.Get_Exit_Status (Proc) /= 0 then
               AKT.Commands.Log.Error (-("editor exited with status{0}"),
                                       Natural'Image (Util.Processes.Get_Exit_Status (Proc)));
               Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure);
            else
               Import_Value (Context, Args.Get_Argument (Context.First_Arg), Path);
            end if;
            Cleanup;

         exception
            when Util.Processes.Process_Error =>
               AKT.Commands.Log.Error (-("cannot execute editor '{0}'"), Editor);
               Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure);
               Cleanup;

            when others =>
               Cleanup;
               raise;

         end;
      end if;
   end Execute;

   --  ------------------------------
   --  Setup the command before parsing the arguments and executing it.
   --  ------------------------------
   overriding
   procedure Setup (Command : in out Command_Type;
                    Config  : in out GNAT.Command_Line.Command_Line_Configuration;
                    Context : in out Context_Type) is
      package GC renames GNAT.Command_Line;
   begin
      Drivers.Command_Type (Command).Setup (Config, Context);
      GC.Define_Switch (Config => Config,
                        Output => Command.Editor'Access,
                        Switch => "-e:",
                        Long_Switch => "--editor=",
                        Argument => "EDITOR",
                        Help => -("Define the editor command to use"));
   end Setup;

end AKT.Commands.Edit;