From c6a3bdfc5e02b9e35b1e0fc5af2d0bf0319681ac Mon Sep 17 00:00:00 2001 From: Jeffrey Armstrong Date: Tue, 30 Mar 2021 15:09:09 -0400 Subject: Added script to scan and load instructions. Instructions now displayed on a page. --- captain/config.f90 | 14 +++++- captain/db.f90 | 56 +++++++++++++++++++++++ captain/example/levitating.conf | 4 ++ captain/external.f90 | 96 +++++++++++++++++++++++++++++++++++++++- captain/gemini.f90 | 8 ++++ captain/levitating-captain.prj | 3 ++ captain/sql/create.sql | 2 +- captain/sql/scan_instructions.sh | 19 ++++++++ captain/templates/index.gmi | 1 + 9 files changed, 199 insertions(+), 4 deletions(-) create mode 100755 captain/sql/scan_instructions.sh (limited to 'captain') diff --git a/captain/config.f90 b/captain/config.f90 index 5ab5930..3d23011 100644 --- a/captain/config.f90 +++ b/captain/config.f90 @@ -34,6 +34,12 @@ implicit none character(*), parameter::STATIC_DIRECTORY_VARIABLE = "static-directory" character(1024)::static_dir + character(*), parameter::INSTRUCTIONS_DIRECTORY_VARIABLE = "instructions-directory" + character(1024)::instructions_dir + + character(*), parameter::SCRIPT_DIRECTORY_VARIABLE = "script-directory" + character(1024)::script_dir + contains subroutine get_variable(str, v) @@ -109,7 +115,13 @@ contains else if(cvariable == STATIC_DIRECTORY_VARIABLE) then static_dir = cvalue - + + else if(cvariable == SCRIPT_DIRECTORY_VARIABLE) then + script_dir = cvalue + + else if(cvariable == INSTRUCTIONS_DIRECTORY_VARIABLE) then + instructions_dir = cvalue + end if end subroutine assign_config diff --git a/captain/db.f90 b/captain/db.f90 index 2dc1cff..b607c39 100644 --- a/captain/db.f90 +++ b/captain/db.f90 @@ -3,6 +3,7 @@ use sqlite implicit none integer, parameter::PLAYER_NAME_LENGTH = 128 + integer, parameter::FILENAME_NAME_LENGTH = 1024 character(1024)::database_file type(c_ptr)::db @@ -135,4 +136,59 @@ contains end function get_player_names + function get_instuctions_count() + implicit none + + type(sqlite3_stmt)::stmt + integer::get_instuctions_count + + get_instuctions_count = 0 + if(stmt%prepare(db, "SELECT COUNT(*) FROM instructions") == SQLITE_OK) then + if(stmt%step() == SQLITE_ROW) then + get_instuctions_count = stmt%column_int(0) + end if + end if + call stmt%finalize() + + end function get_instuctions_count + + function get_instruction_names() result(res) + implicit none + + type(sqlite3_stmt)::stmt + character(len=PLAYER_NAME_LENGTH), dimension(:), pointer::res + integer::i,n + + n = get_instuctions_count() + if(n > 0) then + allocate(res(n)) + if(stmt%prepare(db, "SELECT name FROM instructions ORDER BY name") == SQLITE_OK) then + i = 1 + do while(stmt%step() == SQLITE_ROW .and. i <= n) + call stmt%column_text(0, res(i)) + i = i + 1 + end do + end if + call stmt%finalize() + + end if + + end function get_instruction_names + + subroutine scan_instructions_for_db() + use config + use utilities + use logging + implicit none + + character(len=2048)::cmdline + + call combine_paths(script_dir, "scan_instructions.sh", cmdline) + cmdline = trim(cmdline)//" "//trim(database_file)//" "//trim(instructions_dir)//" 1>/dev/null 2>/dev/null" + + call write_log("Scan Command: "//trim(cmdline)) + call execute_command_line(trim(cmdline), wait=.true.) + + end subroutine scan_instructions_for_db + end module captain_db diff --git a/captain/example/levitating.conf b/captain/example/levitating.conf index a43ea8d..1b647a4 100644 --- a/captain/example/levitating.conf +++ b/captain/example/levitating.conf @@ -18,3 +18,7 @@ uploads-directory = /home/jeff/Workspace/levitating/captain/example/uploads results-directory = /home/jeff/Workspace/levitating/captain/example/results static-directory = /home/jeff/Workspace/levitating/captain/example/static + +script-directory = /home/jeff/Workspace/levitating/captain/sql + +instructions-directory = /home/jeff/Workspace/levitating/captain/example/instructions diff --git a/captain/external.f90 b/captain/external.f90 index 5e64bab..78195f8 100644 --- a/captain/external.f90 +++ b/captain/external.f90 @@ -49,6 +49,52 @@ contains end function generate_players_gemini + function generate_instructions_gemini() result(res) + use captain_db + implicit none + + character(len=:), pointer::res + character(len=PLAYER_NAME_LENGTH), dimension(:), pointer::instruction_names + integer::n, i, nsize + + character(len=3*PLAYER_NAME_LENGTH)::one_player + + n = get_instuctions_count() + + if(n == 0) then + + allocate(character(len=1024) :: res) + res = "None Yet" + + else + + instruction_names => get_instruction_names() + nsize = 1024 + do i = 1, size(instruction_names) + nsize = nsize + 16 + 2*len_trim(instruction_names(i)) + end do + + allocate(character(len=nsize) :: res) + res = "## Instructions" + + do i = 1, n + one_player = "=> /instructions/"//trim(instruction_names(i))//".json "//trim(instruction_names(i)) + if(i == 1) then + res = trim(res)//new_line(res(1:1))//new_line(res(1:1))//trim(one_player) + else + res = trim(res)//new_line(res(1:1))//trim(one_player) + end if + end do + + deallocate(instruction_names) + + end if + + res = trim(res)//new_line(res(1:1))//new_line(res(1:1))//"## Management"// & + new_line(res(1:1))//"=> /instructions/scan.gmi Scan for Instructions" + + end function generate_instructions_gemini + pure function is_input_provided_request(req) use server_response, only: request implicit none @@ -123,10 +169,43 @@ contains (trim(first) == "uploads") .or. & (trim(first) == "results") .or. & (trim(first) == "static") .or. & - (trim(first) == "favicon.txt")) + (trim(first) == "favicon.txt") .or. & + (trim(first) == "instructions")) end function is_request_static + function is_redirect_action(req) + use server_response + implicit none + + class(request), intent(in)::req + logical::is_redirect_action + + is_redirect_action = .false. + if(req%location == "/instructions/scan.gmi") then + is_redirect_action = .true. + end if + + end function is_redirect_action + + function external_redirect_action_request_gemini(req) result(resp) + use captain_db + use server_response + use logging + implicit none + + class(request), intent(in)::req + type(response)::resp + + resp%code = GEMINI_CODE_REDIRECT + + if(req%location == "/instructions/scan.gmi") then + call scan_instructions_for_db() + call resp%set_url("/instructions.gmi") + end if + + end function external_redirect_action_request_gemini + function external_request_static(req) result(resp) use logging, only: write_log use config @@ -177,6 +256,9 @@ contains else if(index(filename, ".txt") /= 0) then resp%body_mimetype = "text/plain" + else if(index(filename, ".json") /= 0) then + resp%body_mimetype = "text/plain" + ! Just a catch-all, whatever... else resp%body_mimetype = "application/octet-stream" @@ -231,6 +313,12 @@ contains call page%assign('title', 'About') + else if(trim(req%location) == "/instructions.gmi") then + + call page%assign('title', 'Build Instructions') + contents => generate_instructions_gemini() + call page%assign('contents', contents) + else call page%assign('title', 'Not Found') @@ -259,7 +347,11 @@ contains class(request), intent(in)::req type(response)::resp - if(is_input_provided_request(req)) then + if(is_redirect_action(req)) then + call write_log("Action request") + resp = external_redirect_action_request_gemini(req) + + else if(is_input_provided_request(req)) then call write_log("Input request") resp = external_input_request_gemini(req) diff --git a/captain/gemini.f90 b/captain/gemini.f90 index 0930d17..46725cb 100644 --- a/captain/gemini.f90 +++ b/captain/gemini.f90 @@ -85,6 +85,9 @@ contains character(1024)::line write(int_text, '(I8)') code + line = trim(adjustl(int_text))//" "//trim(meta) + call write_log("Status line: "//trim(line)) + call write_string(ssl, trim(adjustl(int_text))//" "//trim(meta)//c_carriage_return//c_new_line) end subroutine write_status @@ -290,6 +293,11 @@ contains call req%destroy() call resp%destroy() + + + res = ssl_shutdown(ssl) + res = ssl_free(ssl) + res = ctx_free(ctx) end subroutine handle_request diff --git a/captain/levitating-captain.prj b/captain/levitating-captain.prj index 1fe6cc8..31841f9 100644 --- a/captain/levitating-captain.prj +++ b/captain/levitating-captain.prj @@ -39,6 +39,9 @@ "Files":[{ "filename":"sql/create.sql", "enabled":"1" + },{ + "filename":"sql/scan_instructions.sh", + "enabled":"1" }] },{ "Folders":[], diff --git a/captain/sql/create.sql b/captain/sql/create.sql index 9fd00d0..e8198e4 100644 --- a/captain/sql/create.sql +++ b/captain/sql/create.sql @@ -5,7 +5,7 @@ CREATE TABLE jobs(id INTEGER PRIMARY KEY, player INTEGER DEFAULT NULL, status IN CREATE TABLE tasks(job INTEGER, task INTEGER, status INTEGER, FOREIGN KEY(job) REFERENCES jobs(id)); -CREATE TABLE instructions(id INTEGER PRIMARY KEY, name TEXT NOT NULL, file TEXT NOT NULL); +CREATE TABLE instructions(id INTEGER PRIMARY KEY, name TEXT UNIQUE NOT NULL); CREATE_TABLE available(instruction INTEGER, player INTEGER, \ FOREIGN KEY(instruction) REFERENCES instructions(id), \ diff --git a/captain/sql/scan_instructions.sh b/captain/sql/scan_instructions.sh new file mode 100755 index 0000000..dab98b3 --- /dev/null +++ b/captain/sql/scan_instructions.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +cmds=`tempfile` +echo $cmds + +for filename in $2/*.json; do + # In case it doesn't exist + [ -e "$filename" ] || continue + name=${filename##*/} + echo $name + base=${name%.json} + echo $base + + echo "INSERT OR IGNORE INTO instructions(name) VALUES('$base');" | cat >> $cmds +done + +sqlite3 $1 < $cmds + +rm $cmds diff --git a/captain/templates/index.gmi b/captain/templates/index.gmi index ebc7419..bbcd8d2 100644 --- a/captain/templates/index.gmi +++ b/captain/templates/index.gmi @@ -7,6 +7,7 @@ The Levitating Captain interface for the {{ project }} project. => /releases.gmi Releases => /jobs.gmi Jobs => /players.gmi Players +=> /instructions.gmi Instructions => /about.gmi About {{ contents }} -- cgit v1.2.3