Frecon code: scan.pal

From PROSE Programming Language - Wiki
Jump to: navigation, search
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Scan Routines for Frecon
% Author: Mark R. Bannister
% Date: 19/Dec/2017
%
% Provides routines for scanning subdirectories underneath
% a path and collecting data for the File Reconnaissance Tool
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
~Module
EQUS {[frecon]}

._init
attr/load       P0, [psString], P1, [psPointer], P2, [psBoolean]

func/def        [scan_opendir], &[.scan_opendir], P1, P0, [path]
func/def        [scan_openlast], &[.scan_openlast], P1
func/def        [scan_closedir], &[.scan_closedir], NULL, P1, [handle]
func/def        [scan_start], &[.scan_start], NULL, P2, [cksum]
func/def        [scan], &[.scan_prompt]

error/def	[BadPath], [Bad pathname]
local/rtn

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Open top-level path provided from which scans are to begin and return
% a file object
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.scan_opendir
var/addr        P0, [path]
attr/load       P1, [psString]
var/global      P2, P1, [scan_dir]
attr/direct     P2, P1, P0, P1

.open_handle
%
% Set-up global variable manually
%
obj/def         P2
class/add       P2, [psFileHook], [psStatUnix]
attr/copy       P0, P0, P1
attr/add        P2, [psFilePath], P0
obj/commitref   P2, ![-], [dir_handle], P2
func/rtn        P2

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Open top-level path previously supplied to scan_opendir
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.scan_openlast
var/addr        P0, [scan_dir]
attr/load       P1, [psString]
local/jmp       &[.open_handle]

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Close handle returned by scan_opendir
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.scan_closedir
var/addr        P0, [handle]
attr/addr       P0, P0, [psPointer]
obj/del         P0                      % delete global variable
func/rtn

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Ask question and get Y/N response, where P0 contains the question
% and SCMP will be equal (1) upon return if Y was answered
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.get_yn
reg/load        P14, ![.prose.sys.io.stdin],
                P15, ![.prose.sys.io.stdout]
attr/load       P12, [psStreamIn], P13, [psStreamOut]

.get_choice
%
% Display user prompt
%
attr/mvadd        P15, P13, P0, [? [y/n\]: ]

%
% Grab one line of input
%
.get_stdin
attr/copy       P0, P14, P12

%
% Force to uppercase
%
reg/jmpeq       &[.get_stdin], P0, NULL         % Ignore Ctrl+D
reg/load        A, (P0, #0)
opa/shr         #0x18
opa/and         #0x5f

%
% Test for Y/y
%
reg/jmpeq       &[.yes], A, #0x59

%
% Test for N/n
%
reg/jmpeq       &[.no], A, #0x4e

%
% Sound bell on error
%
attr/add        P15, P13, [\a]
local/jmp       &[.get_choice]

.yes
reg/load        SCMP, #1
local/rtn

.no
reg/load        SCMP, #0
local/rtn

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Prompt use before scan begins to determine if checksums are required
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.scan_prompt
reg/load        P0, [Calculate checksums during the scan]
local/jsr       &[.get_yn]
func/call       NULL, [scan_start], SCMP
func/rtn

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Scan a filesystem - this will overwrite the contents of the existing
% database with a fresh scan from underneath the chosen scan path
%
% Takes a cksum argument that will be TRUE if checksums are also to
% be calculated on all regular files encountered
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.scan_start
%
% Truncate the database and statistics to lose all existing records
%
func/call	NULL, [db_truncate]
func/call       NULL, [stat_reset]

%
% Save initial database record for top-level directory
%
func/bcall      P0, [scan_openlast]
attr/addr       P0, P0
func/bcall	NULL, [db_write_record], P0, #0, #0
func/bcall	NULL, [stat_store], P0
reg/load	P1, (P0)

%
% Walk every file and directory under the top-level and create
% database records for each one
%
var/addr        P5, [cksum]
attr/index      P5, P5, [psBoolean]             % checksums or not?

attr/load	P6, [psInteger]
attr/def	P7, P6, #0			% last record number

reg/load	P8, ![.prose.sys.io.stdout], P9, ![.prose.sys.io.stderr]
class/load	P10, [psStatUnix]
attr/load	P11, [statMode], P12, [psStreamOut], P13, [psFilePath]
reg/load	P14, ![.prose.error.sys.AccessDenied]

attr/add	P8, P12, [\nScanning ...]
error/jmp	&[.access_denied], P14

.loop
reg/load	P2, (P1)
reg/jmpeq	&[.loop_end], P2, NULL
class/add	P2, P10

%
% Increment record number and write a new record to the database file
%
op/incr		P7
attr/def	P3, P6, P7
func/bcall	NULL, [db_write_record], P2, P3, P5
func/bcall	NULL, [stat_store], P2

%
% If there are child nodes, walk into subdirectory
%
reg/load	P2, (P2)
reg/jmpeq	&[.loop], P2, NULL	% no child nodes, continue
stack/push	P1
reg/move	P1, P2
attr/add	P8, P12, [.]
local/jmp	&[.loop]

.access_denied
error/clr
error/jmp	&[.access_denied], P14
reg/jmpne	&[.access_p2], P2, NULL

.access_p1
attr/copy	P2, P1, P13
local/jmp	&[.access_error]

.access_p2
attr/copy	P2, P2, P13

.access_error
reg/copy	PUSH, [\nError: access denied: ], P2, [\n]
attr/add	P9, P12, PULL
local/jmp	&[.loop]

.loop_end
reg/jmpeq	&[.scan_stop], PEEK, LOCK
stack/pull	P1
local/jmp	&[.loop]

.scan_stop
reg/clr         P1
func/call       NULL, [scan_closedir], P0
func/rtn