Frecon code: pathdiff.pal

From PROSE Programming Language - Wiki
Jump to: navigation, search
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Difference Routines For Frecon
% Author: Mark R. Bannister
% Date: 08/Feb/2018
%
% Provides routines that will find the differences between a directory
% path when last scanned vs. current state
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
~Module
EQUS {[frecon]}

._init
func/def        [changes], &[.changes]
func/def        [path_compare], &[.path_compare], NULL, [psPointer], [file]
local/rtn

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Find and report on the differences between the DB and the current path
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.changes
attr/load       P14, [psStreamOut]
reg/load        P15, ![.prose.sys.io.stdout]

func/bcall      P0, [db_num_records]
reg/jmpgt       &[.db_to_objects], P0, #0

attr/add        P15, P14, [No records found to compare.\n]
func/call       NULL, [get_enter]
func/rtn

.db_to_objects
%
% Load records from database into the nexus under .prose.db
%
func/bcall      NULL, [db_obj_all_records]

%
% Now scan path again, comparing with objects loaded in under .prose.db
%
func/bcall      P0, [scan_openlast]
attr/addr       P0, P0
func/bcall      NULL, [path_compare], P0
reg/load        P1, (P0)
class/load      P10, [psStatUnix]

%
% Walk every file and directory under the top-level and compare
% with the entries under .prose.db
%
.loop
reg/load        P2, (P1)
reg/jmpeq       &[.loop_end], P2, NULL
class/add       P2, P10
func/bcall      NULL, [path_compare], 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
local/jmp       &[.loop]

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

.compare_stop
reg/clr         P1

%
% Any entries left in .prose.db have been deleted (or renamed) since
%
reg/load        P8, (![.prose.db])
reg/jmpeq       &[.compare_rtn], P8, NULL
attr/load       P3, [pn]

.missing_loop
reg/load        P1, (P8)
reg/jmpeq       &[.compare_rtn], P1, NULL
attr/copy       P2, P1, P3
attr/mvadd      P15, P14, [FILE REMOVED:\n  ], P2, [\n\n]
local/jmp       &[.missing_loop]

.compare_rtn
func/call       NULL, [scan_closedir], P0
func/call       NULL, [get_enter]
func/rtn

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Compare single path with previous entry in .prose.db
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.path_compare
var/addr        P0, [file]
attr/addr       P0, P0, [psPointer]
class/test      P0, [psFileHook]

attr/load       P12, [psString], P13, [psInteger], P14, [psStreamOut]
reg/load        P15, ![.prose.sys.io.stdout]

%
% Copy psFilePath attribute if this is the top-level file object
% Copy psShortPath attribute if this is a subordinate file object
%
eq: attr/load   P2, [psFilePath]
ne: attr/load   P2, [psShortPath]

attr/copy       P1, P0, P2
error/jmp       &[.new_file], ![.prose.error.sys.NoEntry]
obj/child       P2, ![.prose.db], P1

%
% If we got here then the file previously existed, so now check
% to see if any file attributes have changed
%
obj/child       P3, P2, [mode]                  % file mode
obj/child       P4, P2, [size]                  % file size in bytes
obj/child       P5, P2, [uid]                   % owner UID
obj/child       P6, P2, [owner]                 % owner name
obj/child       P7, P2, [gid]                   % group GID
obj/child       P8, P2, [group]                 % group name
obj/child       P9, P2, [mtime]                 % file modification time
obj/child       P10, P2, [sha]                  % SHA256 checksum

%
% Compare each attribute to see what changed
%
reg/clr
attr/cmp        P0, [statMode], P3, P13
attr/cmp        P0, [statSize], P4, P13
attr/cmp        P0, [statUid], P5, P13
attr/cmp        P0, [statUidName], P6, P12
attr/cmp        P0, [statGid], P7, P13
attr/cmp        P0, [statGidName], P8, P12

%
% Strip subsecond component from last modify timestamp before comparing
%
var/local       P11, [psTime], [mtime]
attr/direct     P11, [psTime], P0, [statLastModify]
attr/copy       P11, P11, [psTimeSeconds]
var/local       P11, P13, [mtint], P11
attr/cmp        P11, P13, P9, [psTimeSeconds]

%
% If SHA256 checksum is non-zero, compute SHA for the current file and compare
%
stack/push      SFLG
reg/copy        P11, []
reg/save        P11, (#32)
attr/copy       P10, P10, P12
reg/cmp         P10, P11
ne: func/bcall  P11, [sha256], P0
stack/pull      SFLG
reg/cmp         P10, P11

%
% Report differences
%
reg/load        A, SFLG
reg/jmpeq       &[.delete_db_obj], A, #0b11111111
attr/mvadd      P15, P14, [FILE CHANGED:\n  ], P1, [\n]

reg/load        P1, A
opa/and         #0b10000000                     % has file mode changed?
reg/jsreq       &[.mode_changed], A, #0
op/and          A, P1, #0b01000000              % has file size changed?
reg/jsreq       &[.size_changed], A, #0
op/and          A, P1, #0b00100000              % has UID changed?
reg/jsreq       &[.uid_changed], A, #0
op/and          A, P1, #0b00010000              % has owner name changed?
reg/jsreq       &[.owner_changed], A, #0
op/and          A, P1, #0b00001000              % has GID changed?
reg/jsreq       &[.gid_changed], A, #0
op/and          A, P1, #0b00000100              % has group name changed?
reg/jsreq       &[.group_changed], A, #0
op/and          A, P1, #0b00000010              % has modification time changed?
reg/jsreq       &[.mtime_changed], A, #0
op/and          A, P1, #0b00000001              % has SHA256 checksum changed?
reg/jsreq       &[.sha_changed], A, #0

attr/add        P15, P14, [\n]

.delete_db_obj
%
% Delete object from .prose.db so that come the end of the scan the
% only objects remaining are those that have been deleted (or renamed)
%
tree/del        P2
func/rtn

.mode_changed
%
% Report change of file mode
%
attr/index      PUSH, P0, [statMode]
reg/conv        PUSH, PULL, #8
attr/index      PUSH, P3, P13
reg/conv        PUSH, PULL, #8
attr/mvadd      P15, P14, [  MODE ], PULL, [ => ], PULL, [\n]
local/rtn

.size_changed
%
% Report change of file size
%
attr/copy       PUSH, P0, [statSize]
attr/copy       PUSH, P4, P13
attr/mvadd      P15, P14, [  SIZE ], PULL, [ => ], PULL, [\n]
local/rtn

.uid_changed
%
% Report change of UID
%
attr/copy       PUSH, P0, [statUid]
attr/copy       PUSH, P5, P13
attr/mvadd      P15, P14, [  UID ], PULL, [ => ], PULL, [\n]
local/rtn

.owner_changed
%
% Report change of owner name
%
attr/copy       PUSH, P0, [statUidName]
attr/copy       PUSH, P6, P12
attr/mvadd      P15, P14, [  OWNER ], PULL, [ => ], PULL, [\n]
local/rtn

.gid_changed
%
% Report change of GID
%
attr/copy       PUSH, P0, [statGid]
attr/copy       PUSH, P7, P13
attr/mvadd      P15, P14, [  GID ], PULL, [ => ], PULL, [\n]
local/rtn

.group_changed
%
% Report change of group name
%
attr/copy       PUSH, P0, [statGidName]
attr/copy       PUSH, P8, P12
attr/mvadd      P15, P14, [  GROUP ], PULL, [ => ], PULL, [\n]
local/rtn

.mtime_changed
%
% Report change of group name
%
attr/copy       PUSH, P0, [statLastModify]
attr/copy       PUSH, P9, [psTime]
attr/mvadd      P15, P14, [  MTIME ], PULL, [ => ], PULL, [\n]
local/rtn

.sha_changed
%
% Report change of SHA256 checksum
%
func/bcall      P10, [sha256_convert], P10
func/bcall      P11, [sha256_convert], P11
attr/mvadd      P15, P14, [  SHA256 ], P10, [ =>\n         ], P11, [\n]
local/rtn

.convert_sha
%
% Convert SHA256 in register P10 into hex notation
%
reg/copy        P11, []
reg/save        P11, (#64)
reg/clr         A

.sha_loop
reg/copy        PUSH, (P10, A, #4)
attr/import     P10, [psIndex], PULL
reg/conv        P10, P10, #16

.new_file
%
% If we got here then a file in the filesystem is new and did
% not exist the last time we did a scan
%
error/clr
attr/mvadd      P15, P14, [NEW FILE:\n  ], P1, [\n\n]
func/rtn