Frecon code: pathdiff.pal
From PROSE Programming Language - Wiki
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % 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