with GNAT.Command_Line; with GNAT.Strings; with Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Ada.Directories; with Ada.Strings.Unbounded; with Simple_Time; with CSV; with Candidates; procedure STV is package ACom renames Ada.Command_Line; package GCom renames GNAT.Command_Line; package GStr renames GNAT.Strings; package File renames Ada.Directories; package SU renames Ada.Strings.Unbounded; use type File.File_Kind; use type SU.Unbounded_String; use type Simple_Time.Time; Config : GCom.Command_Line_Configuration; Further_Help : String := "Try ""stv --help"" for more information."; -- I'm not fond of accesses to string accesses Verbose : aliased Boolean; Version : aliased Boolean; Help : aliased Boolean; Candidate_File : aliased GStr.String_Access; Preference_File : aliased GStr.String_Access; Output_Dir : aliased GStr.String_Access; Number_To_Elect : aliased Integer; State_String : aliased GStr.String_Access; State : Candidates.State_Name; Start_Time, Finish_Time : Simple_Time.Time; Main_Log, Log_Msg : SU.Unbounded_String; Log_File : File_Type; Candidate_List : Candidates.Candidate_Vector; Above_Ballot : Candidates.Above_Line_Ballot; Below_Ballot : Candidates.Below_Line_Ballot; begin GCom.Define_Switch (Config => Config, Output => Verbose'Access, Switch => "-v", Long_Switch => "--verbose", Help => "chatty output on stderr"); GCom.Define_Switch (Config => Config, Output => Version'Access, Switch => "-V", Long_Switch => "--version", Help => "show version number"); -- technically not required, but included so -- it will show up in the help output GCom.Define_Switch (Config => Config, Output => Help'Access, Switch => "-h", Long_Switch => "--help", Help => "show this help information"); GCom.Define_Switch (Config => Config, Output => Candidate_File'Access, Switch => "-c:", Long_Switch => "--candidates=", Help => ".csv file containing AEC candidate data"); GCom.Define_Switch (Config => Config, Output => Preference_File'Access, Switch => "-p:", Long_Switch => "--preference=", Help => ".csv file containing AEC formal preferences"); GCom.Define_Switch (Config => Config, Output => Output_Dir'Access, Switch => "-o:", Long_Switch => "--outdir=", Help => "new directory to output count logging"); GCom.Define_Switch (Config => Config, Output => Number_To_Elect'Access, Switch => "-e:", Long_Switch => "--elect=", Help => "number of candidates to elect"); GCom.Define_Switch (Config => Config, Output => State_String'Access, Switch => "-s:", Long_Switch => "--state=", Help => "state or territory the data corresponds to"); GCom.Set_Usage (Config => Config, Usage => "[switches]", Help => "Note that the -c, -p, -o, -e, -s options are all" & ASCII.LF & "required for normal operation." & ASCII.LF); -- parse options begin GCom.Getopt (Config); exception when GCom.Exit_From_Command_Line => ACom.Set_Exit_Status (ACom.Failure); return; when GCom.Invalid_Switch => ACom.Set_Exit_Status (ACom.Failure); return; end; -- version display functionality if Version then Put_Line ("Australian STV Counter v0.2"); ACom.Set_Exit_Status (ACom.Failure); return; end if; -- check candidate data option is valid if Candidate_File.all = "" then Put_Line ("Candidate data file not provided." & ASCII.LF & Further_Help); ACom.Set_Exit_Status (ACom.Failure); return; end if; if not File.Exists (Candidate_File.all) then Put_Line ("Candidate data file does not exist." & ASCII.LF & Further_Help); ACom.Set_Exit_Status (ACom.Failure); return; end if; if File.Kind (Candidate_File.all) /= File.Ordinary_File then Put_Line ("Candidate data file name appears to refer to a directory or special file." & ASCII.LF & Further_Help); ACom.Set_Exit_Status (ACom.Failure); return; end if; -- check preference data option is valid if Preference_File.all = "" then Put_Line ("Preference data file not provided." & ASCII.LF & Further_Help); ACom.Set_Exit_Status (ACom.Failure); return; end if; if not File.Exists (Preference_File.all) then Put_Line ("Preference data file does not exist." & ASCII.LF & Further_Help); ACom.Set_Exit_Status (ACom.Failure); return; end if; if File.Kind (Preference_File.all) /= File.Ordinary_File then Put_Line ("Preference data file name appears to refer to a directory or special file." & ASCII.LF & Further_Help); ACom.Set_Exit_Status (ACom.Failure); return; end if; -- check output directory option is valid if Output_Dir.all = "" then Put_Line ("Output logging directory not provided." & ASCII.LF & Further_Help); ACom.Set_Exit_Status (ACom.Failure); return; end if; if File.Exists (Output_Dir.all) then Put_Line ("Output logging directory already exists." & ASCII.LF & Further_Help); ACom.Set_Exit_Status (ACom.Failure); return; end if; -- check state option is valid begin State := Candidates.State_Name'Value (State_String.all); exception when Constraint_Error => Put_Line ("Invalid State/Territory or State/Territory not provided." & ASCII.LF & Further_Help); ACom.Set_Exit_Status (ACom.Failure); return; end; -- set up logging File.Create_Directory (Output_Dir.all); Start_Time := Simple_Time.Now; Main_Log := SU.To_Unbounded_String (Output_Dir.all & "/" & "log.txt"); Log_Msg := SU.To_Unbounded_String ("Started election count at " & Simple_Time.To_String (Start_Time)); Create (Log_File, Append_File, SU.To_String (Main_Log)); Put_Line (Log_File, SU.To_String (Log_Msg)); Close (Log_File); if Verbose then Put_Line (Standard_Error, SU.To_String (Log_Msg)); end if; -- set up the election processing if Verbose then Put_Line (Standard_Error, "Reading candidate data..."); end if; Candidates.Read_Candidates (Candidate_File.all, State, Candidate_List); Candidates.Generate_Ballots (Candidate_List, Above_Ballot, Below_Ballot); if Verbose then Put_Line (Standard_Error, "Reading preference data..."); end if; -- read in preference data here if Verbose then Put_Line (Standard_Error, "Done." & ASCII.LF); Put_Line (Standard_Error, "Setting up election..."); end if; -- set up election here if Verbose then Put_Line (Standard_Error, "Done." & ASCII.LF); end if; -- run the show if Verbose then Put_Line (Standard_Error, "Running..." & ASCII.LF); end if; -- run election here if Verbose then New_Line (Standard_Error); end if; -- finish up logging Finish_Time := Simple_Time.Now; Log_Msg := SU.To_Unbounded_String ("Finished election count at " & Simple_Time.To_String (Finish_Time) & ASCII.LF & Duration'Image (Finish_Time - Start_Time) & " seconds elapsed." & ASCII.LF); Open (Log_File, Append_File, SU.To_String (Main_Log)); Put_Line (Log_File, SU.To_String (Log_Msg)); Close (Log_File); if Verbose then Put_Line (Standard_Error, SU.To_String (Log_Msg)); end if; end STV;