From 0c0643d5d7ac8550708c7438e10991c83c51f73f Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 21 Oct 2025 16:27:17 -0500 Subject: [PATCH] scripts: Adopted self-parsing script for dgbflags/err.py encoding This has just proven much easier to tweak in dbgtag.py, so adopting the same self-parsing pattern in dbgflags.py/dbgerr.py. This makes editing easier by (1) not needing to worry about parens/quotes/commas, and (2) allowing for non-python expressions, such as the mode flags in dbgflags.py. The only concern is script startup may be slightly slower, but we really don't care. --- scripts/dbgerr.py | 162 +++++++++---- scripts/dbgflags.py | 562 ++++++++++++++++++++++++++------------------ 2 files changed, 444 insertions(+), 280 deletions(-) diff --git a/scripts/dbgerr.py b/scripts/dbgerr.py index aa9d7f3a..6ada8c87 100755 --- a/scripts/dbgerr.py +++ b/scripts/dbgerr.py @@ -4,26 +4,73 @@ if __name__ == "__main__": __import__('sys').path.pop(0) +import functools as ft -ERRS = [ - ('OK', 0, "No error" ), - ('UNKNOWN', -1, "Unknown error" ), - ('INVAL', -22, "Invalid parameter" ), - ('NOTSUP', -95, "Operation not supported" ), - ('IO', -5, "Error during device operation" ), - ('CORRUPT', -84, "Corrupted" ), - ('NOENT', -2, "No directory entry" ), - ('EXIST', -17, "Entry already exists" ), - ('NOTDIR', -20, "Entry is not a dir" ), - ('ISDIR', -21, "Entry is a dir" ), - ('NOTEMPTY', -39, "Dir is not empty" ), - ('FBIG', -27, "File too large" ), - ('NOSPC', -28, "No space left on device" ), - ('NOMEM', -12, "No more memory available" ), - ('NOATTR', -61, "No data/attr available" ), - ('NAMETOOLONG', -36, "File name too long" ), - ('RANGE', -34, "Result out of range" ), -] + +# Error codes +ERR_OK = 0 # No error +ERR_UNKNOWN = -1 # Unknown error +ERR_INVAL = -22 # Invalid parameter +ERR_NOTSUP = -95 # Operation not supported +ERR_IO = -5 # Error during device operation +ERR_CORRUPT = -84 # Corrupted +ERR_NOENT = -2 # No directory entry +ERR_EXIST = -17 # Entry already exists +ERR_NOTDIR = -20 # Entry is not a dir +ERR_ISDIR = -21 # Entry is a dir +ERR_NOTEMPTY = -39 # Dir is not empty +ERR_FBIG = -27 # File too large +ERR_NOSPC = -28 # No space left on device +ERR_NOMEM = -12 # No more memory available +ERR_NOATTR = -61 # No data/attr available +ERR_NAMETOOLONG = -36 # File name too long +ERR_RANGE = -34 # Result out of range + + +# self-parsing error codes +class Err: + def __init__(self, name, code, help): + self.name = name + self.code = code + self.help = help + + def __repr__(self): + return 'Err(%r, %r, %r)' % ( + self.name, + self.code, + self.help) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __hash__(self): + return hash(self.name) + + def line(self): + return ('LFS3_%s' % self.name, '%d' % self.code, self.help) + +@ft.cache +def errs(): + # parse our script's source to figure out errs + import inspect + import re + errs = [] + err_pattern = re.compile( + '^(?PERR_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P.*)$') + for line in (inspect.getsource(inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = err_pattern.match(line) + if m: + errs.append(Err( + m.group('name'), + globals()[m.group('name')], + m.group('help'))) + return errs def main(errs, *, @@ -31,51 +78,64 @@ def main(errs, *, import builtins list_, list = list, builtins.list + # find errs + errs__ = globals()['errs']() + lines = [] # list all known error codes if list_: - for n, e, h in ERRS: - lines.append(('LFS3_ERR_'+n, str(e), h)) + for e in errs__: + lines.append(e.line()) - # find these errors + # find errs by name or value else: - def find_err(err): + for e_ in errs: + found = False # find by LFS3_ERR_+name - for n, e, h in ERRS: - if 'LFS3_ERR_'+n == err.upper(): - return n, e, h + for e in errs__: + if 'LFS3_%s' % e.name.upper() == e_.upper(): + lines.append(e.line()) + found = True + if found: + continue # find by ERR_+name - for n, e, h in ERRS: - if 'ERR_'+n == err.upper(): - return n, e, h + for e in errs__: + if e.name.upper() == e_.upper(): + lines.append(e.line()) + found = True + if found: + continue # find by name - for n, e, h in ERRS: - if n == err.upper(): - return n, e, h + for e in errs__: + if e.name.split('_', 1)[1] == e_.upper(): + lines.append(e.line()) + found = True + if found: + continue # find by E+name - for n, e, h in ERRS: - if 'E'+n == err.upper(): - return n, e, h + for e in errs__: + if 'E%s' % e.name.split('_', 1)[1].upper() == e_.upper(): + lines.append(e.line()) + found = True + if found: + continue try: # find by err code - for n, e, h in ERRS: - if e == int(err, 0): - return n, e, h + for e in errs__: + if e.code == int(e_, 0): + lines.append(e.line()) + found = True + if found: + continue # find by negated err code - for n, e, h in ERRS: - if e == -int(err, 0): - return n, e, h + for e in errs__: + if e.code == -int(e_, 0): + lines.append(e.line()) + found = True + if found: + continue except ValueError: - pass - # not found - raise KeyError(err) - - for err in errs: - try: - n, e, h = find_err(err) - lines.append(('LFS3_ERR_'+n, str(e), h)) - except KeyError: - lines.append(('?', err, 'Unknown err code')) + lines.append(('?', e_, 'Unknown err code')) # first find widths w = [0, 0] diff --git a/scripts/dbgflags.py b/scripts/dbgflags.py index d67b17f0..ccf48693 100755 --- a/scripts/dbgflags.py +++ b/scripts/dbgflags.py @@ -5,268 +5,372 @@ if __name__ == "__main__": __import__('sys').path.pop(0) import collections as co +import functools as ft -FILTERS = [ - (['--o', '--open'], 'O', "Filter by LFS3_O_* flags."), - (['--a', '--attr'], 'A', "Filter by LFS3_A_* flags."), - (['--f', '--format'], 'F', "Filter by LFS3_F_* flags."), - (['--m', '--mount'], 'M', "Filter by LFS3_M_* flags."), - ('--gc', 'GC', "Filter by LFS3_GC_* flags."), - (['--i', '--info'], 'I', "Filter by LFS3_I_* flags."), - (['--t', '--traversal'], 'T', "Filter by LFS3_T_* flags."), - ('--alloc', 'ALLOC', "Filter by LFS3_ALLOC_* flags."), - (['--rc', '--rcompat'], 'RCOMPAT', "Filter by LFS3_RCOMPAT_* flags."), - (['--wc', '--wcompat'], 'WCOMPAT', "Filter by LFS3_WCOMPAT_* flags."), - (['--oc', '--ocompat'], 'OCOMPAT', "Filter by LFS3_OCOMPAT_* flags."), -] +# Flag prefixes +PREFIX_O = ['--o', '--open'] # Filter by LFS3_O_* flags +PREFIX_A = ['--a', '--attr'] # Filter by LFS3_A_* flags +PREFIX_F = ['--f', '--format'] # Filter by LFS3_F_* flags +PREFIX_M = ['--m', '--mount'] # Filter by LFS3_M_* flags +PREFIX_GC = ['--gc'] # Filter by LFS3_GC_* flags +PREFIX_I = ['--i', '--info'] # Filter by LFS3_I_* flags +PREFIX_T = ['--t', '--trv'] # Filter by LFS3_T_* flags +PREFIX_ALLOC = ['--alloc'] # Filter by LFS3_ALLOC_* flags +PREFIX_RCOMPAT = ['--rc', '--rcompat'] # Filter by LFS3_RCOMPAT_* flags +PREFIX_WCOMPAT = ['--wc', '--wcompat'] # Filter by LFS3_WCOMPAT_* flags +PREFIX_OCOMPAT = ['--oc', '--ocompat'] # Filter by LFS3_OCOMPAT_* flags -FLAGS = [ - # File open flags - ('O_MODE', 3, "The file's access mode" ), - ('^_RDONLY', 0, "Open a file as read only" ), - ('^_WRONLY', 1, "Open a file as write only" ), - ('^_RDWR', 2, "Open a file as read and write" ), - ('O_CREAT', 0x00000004, "Create a file if it does not exist" ), - ('O_EXCL', 0x00000008, "Fail if a file already exists" ), - ('O_TRUNC', 0x00000010, "Truncate the existing file to zero size" ), - ('O_APPEND', 0x00000020, "Move to end of file on every write" ), - ('O_FLUSH', 0x00000040, "Flush data on every write" ), - ('O_SYNC', 0x00000080, "Sync metadata on every write" ), - ('O_DESYNC', 0x04000000, "Do not sync or recieve file updates" ), - ('O_CKMETA', 0x00001000, "Check metadata checksums" ), - ('O_CKDATA', 0x00002000, "Check metadata + data checksums" ), - ('o_WRSET', 3, "Open a file as an atomic write" ), - ('o_TYPE', 0xf0000000, "The file's type" ), - ('^_REG', 0x10000000, "Type = regular-file" ), - ('^_DIR', 0x20000000, "Type = directory" ), - ('^_STICKYNOTE', 0x30000000, "Type = stickynote" ), - ('^_BOOKMARK', 0x40000000, "Type = bookmark" ), - ('^_ORPHAN', 0x50000000, "Type = orphan" ), - ('^_TRAVERSAL', 0x60000000, "Type = traversal" ), - ('^_UNKNOWN', 0x70000000, "Type = unknown" ), - ('o_ZOMBIE', 0x08000000, "File has been removed" ), - ('o_UNCREAT', 0x02000000, "File does not exist yet" ), - ('o_UNSYNC', 0x01000000, "File's metadata does not match disk" ), - ('o_UNCRYST', 0x00800000, "File's leaf not fully crystallized" ), - ('o_UNGRAFT', 0x00400000, "File's leaf does not match disk" ), - ('o_UNFLUSH', 0x00200000, "File's cache does not match disk" ), +# File open flags +O_MODE = 3 # -m The file's access mode +O_RDONLY = 0 # -^ Open a file as read only +O_WRONLY = 1 # -^ Open a file as write only +O_RDWR = 2 # -^ Open a file as read and write +O_CREAT = 0x00000004 # -- Create a file if it does not exist +O_EXCL = 0x00000008 # -- Fail if a file already exists +O_TRUNC = 0x00000010 # -- Truncate the existing file to zero size +O_APPEND = 0x00000020 # -- Move to end of file on every write +O_FLUSH = 0x00000040 # y- Flush data on every write +O_SYNC = 0x00000080 # y- Sync metadata on every write +O_DESYNC = 0x04000000 # -- Do not sync or recieve file updates +O_CKMETA = 0x00001000 # -- Check metadata checksums +O_CKDATA = 0x00002000 # -- Check metadata + data checksums - # Custom attribute flags - ('A_MODE', 3, "The attr's access mode" ), - ('^_RDONLY', 0, "Open an attr as read only" ), - ('^_WRONLY', 1, "Open an attr as write only" ), - ('^_RDWR', 2, "Open an attr as read and write" ), - ('A_LAZY', 0x04, "Only write attr if file changed" ), +o_WRSET = 3 # i- Open a file as an atomic write +o_TYPE = 0xf0000000 # im The file's type +o_REG = 0x10000000 # i^ Type = regular-file +o_DIR = 0x20000000 # i^ Type = directory +o_STICKYNOTE = 0x30000000 # i^ Type = stickynote +o_BOOKMARK = 0x40000000 # i^ Type = bookmark +o_ORPHAN = 0x50000000 # i^ Type = orphan +o_TRAVERSAL = 0x60000000 # i^ Type = traversal +o_UNKNOWN = 0x70000000 # i^ Type = unknown +o_ZOMBIE = 0x08000000 # i- File has been removed +o_UNCREAT = 0x02000000 # i- File does not exist yet +o_UNSYNC = 0x01000000 # i- File's metadata does not match disk +o_UNCRYST = 0x00800000 # i- File's leaf not fully crystallized +o_UNGRAFT = 0x00400000 # i- File's leaf does not match disk +o_UNFLUSH = 0x00200000 # i- File's cache does not match disk - # Filesystem format flags - ('F_MODE', 1, "Format's access mode" ), - ('^_RDWR', 0, "Format the filesystem as read and write" ), - ('F_REVDBG', 0x00000010, "Add debug info to revision counts" ), - ('F_REVNOISE', 0x00000020, "Add noise to revision counts" ), - ('F_CKPROGS', 0x00080000, "Check progs by reading back progged data" ), - ('F_CKFETCHES', 0x00100000, "Check block checksums before first use" ), - ('F_CKMETAPARITY', 0x00200000, "Check metadata tag parity bits" ), - ('F_CKDATACKSUMS', 0x00800000, "Check data checksums on reads" ), +# Custom attribute flags +A_MODE = 3 # -m The attr's access mode +A_RDONLY = 0 # -^ Open an attr as read only +A_WRONLY = 1 # -^ Open an attr as write only +A_RDWR = 2 # -^ Open an attr as read and write +A_LAZY = 0x04 # -- Only write attr if file changed - ('F_CKMETA', 0x00001000, "Check metadata checksums" ), - ('F_CKDATA', 0x00002000, "Check metadata + data checksums" ), +# Filesystem format flags +F_MODE = 1 # -m Format's access mode +F_RDWR = 0 # -^ Format the filesystem as read and write +F_REVDBG = 0x00000010 # y- Add debug info to revision counts +F_REVNOISE = 0x00000020 # y- Add noise to revision counts +F_CKPROGS = 0x00080000 # y- Check progs by reading back progged data +F_CKFETCHES = 0x00100000 # y- Check block checksums before first use +F_CKMETAPARITY = 0x00200000 # y- Check metadata tag parity bits +F_CKDATACKSUMS = 0x00800000 # y- Check data checksums on reads - ('F_GBMAP', 0x01000000, "Use the global on-disk block-map" ), +F_CKMETA = 0x00001000 # y- Check metadata checksums +F_CKDATA = 0x00002000 # y- Check metadata + data checksums - # Filesystem mount flags - ('M_MODE', 1, "Mount's access mode" ), - ('^_RDWR', 0, "Mount the filesystem as read and write" ), - ('^_RDONLY', 1, "Mount the filesystem as read only" ), - ('M_FLUSH', 0x00000040, "Open all files with LFS3_O_FLUSH" ), - ('M_SYNC', 0x00000080, "Open all files with LFS3_O_SYNC" ), - ('M_REVDBG', 0x00000010, "Add debug info to revision counts" ), - ('M_REVNOISE', 0x00000020, "Add noise to revision counts" ), - ('M_CKPROGS', 0x00080000, "Check progs by reading back progged data" ), - ('M_CKFETCHES', 0x00100000, "Check block checksums before first use" ), - ('M_CKMETAPARITY', 0x00200000, "Check metadata tag parity bits" ), - ('M_CKDATACKSUMS', 0x00800000, "Check data checksums on reads" ), +F_GBMAP = 0x01000000 # y- Use the global on-disk block-map - ('M_MKCONSISTENT', 0x00000100, "Make the filesystem consistent" ), - ('M_REPOPLOOKAHEAD', - 0x00000200, "Repopulate lookahead buffer" ), - ('M_REPOPGBMAP', 0x00000400, "Repopulate the gbmap" ), - ('M_COMPACTMETA', 0x00000800, "Compact metadata logs" ), - ('M_CKMETA', 0x00001000, "Check metadata checksums" ), - ('M_CKDATA', 0x00002000, "Check metadata + data checksums" ), +# Filesystem mount flags +M_MODE = 1 # -m Mount's access mode +M_RDWR = 0 # -^ Mount the filesystem as read and write +M_RDONLY = 1 # -^ Mount the filesystem as read only +M_FLUSH = 0x00000040 # y- Open all files with LFS3_O_FLUSH +M_SYNC = 0x00000080 # y- Open all files with LFS3_O_SYNC +M_REVDBG = 0x00000010 # y- Add debug info to revision counts +M_REVNOISE = 0x00000020 # y- Add noise to revision counts +M_CKPROGS = 0x00080000 # y- Check progs by reading back progged data +M_CKFETCHES = 0x00100000 # y- Check block checksums before first use +M_CKMETAPARITY = 0x00200000 # y- Check metadata tag parity bits +M_CKDATACKSUMS = 0x00800000 # y- Check data checksums on reads - # GC flags - ('GC_MKCONSISTENT',0x00000100, "Make the filesystem consistent" ), - ('GC_REPOPLOOKAHEAD', - 0x00000200, "Repopulate lookahead buffer" ), - ('GC_REPOPGBMAP', 0x00000400, "Repopulate the gbmap" ), - ('GC_COMPACTMETA', 0x00000800, "Compact metadata logs" ), - ('GC_CKMETA', 0x00001000, "Check metadata checksums" ), - ('GC_CKDATA', 0x00002000, "Check metadata + data checksums" ), +M_MKCONSISTENT = 0x00000100 # y- Make the filesystem consistent +M_REPOPLOOKAHEAD \ + = 0x00000200 # y- Repopulate lookahead buffer +M_REPOPGBMAP = 0x00000400 # y- Repopulate the gbmap +M_COMPACTMETA = 0x00000800 # y- Compact metadata logs +M_CKMETA = 0x00001000 # y- Check metadata checksums +M_CKDATA = 0x00002000 # y- Check metadata + data checksums - # Filesystem info flags - ('I_RDONLY', 0x00000001, "Mounted read only" ), - ('I_FLUSH', 0x00000040, "Mounted with LFS3_M_FLUSH" ), - ('I_SYNC', 0x00000080, "Mounted with LFS3_M_SYNC" ), - ('I_REVDBG', 0x00000010, "Mounted with LFS3_M_REVDBG" ), - ('I_REVNOISE', 0x00000020, "Mounted with LFS3_M_REVNOISE" ), - ('I_CKPROGS', 0x00080000, "Mounted with LFS3_M_CKPROGS" ), - ('I_CKFETCHES', 0x00100000, "Mounted with LFS3_M_CKFETCHES" ), - ('I_CKMETAPARITY', 0x00200000, "Mounted with LFS3_M_CKMETAPARITY" ), - ('I_CKDATACKSUMS', 0x00800000, "Mounted with LFS3_M_CKDATACKSUMS" ), +# GC flags +GC_MKCONSISTENT = 0x00000100 # -- Make the filesystem consistent +GC_REPOPLOOKAHEAD \ + = 0x00000200 # -- Repopulate lookahead buffer +GC_REPOPGBMAP = 0x00000400 # -- Repopulate the gbmap +GC_COMPACTMETA = 0x00000800 # -- Compact metadata logs +GC_CKMETA = 0x00001000 # -- Check metadata checksums +GC_CKDATA = 0x00002000 # -- Check metadata + data checksums - ('I_MKCONSISTENT', 0x00000100, "Filesystem needs mkconsistent to write" ), - ('I_REPOPLOOKAHEAD', - 0x00000200, "Lookahead buffer is not full" ), - ('I_REPOPGBMAP', 0x00000400, "The gbmap is not full" ), - ('I_COMPACTMETA', 0x00000800, "Filesystem may have uncompacted metadata" ), - ('I_CKMETA', 0x00001000, "Metadata checksums not checked recently" ), - ('I_CKDATA', 0x00002000, "Data checksums not checked recently" ), +# Filesystem info flags +I_RDONLY = 0x00000001 # -- Mounted read only +I_FLUSH = 0x00000040 # -- Mounted with LFS3_M_FLUSH +I_SYNC = 0x00000080 # -- Mounted with LFS3_M_SYNC +I_REVDBG = 0x00000010 # -- Mounted with LFS3_M_REVDBG +I_REVNOISE = 0x00000020 # -- Mounted with LFS3_M_REVNOISE +I_CKPROGS = 0x00080000 # -- Mounted with LFS3_M_CKPROGS +I_CKFETCHES = 0x00100000 # -- Mounted with LFS3_M_CKFETCHES +I_CKMETAPARITY = 0x00200000 # -- Mounted with LFS3_M_CKMETAPARITY +I_CKDATACKSUMS = 0x00800000 # -- Mounted with LFS3_M_CKDATACKSUMS - ('I_GBMAP', 0x01000000, "Global on-disk block-map in use" ), +I_MKCONSISTENT = 0x00000100 # -- Filesystem needs mkconsistent to write +I_REPOPLOOKAHEAD \ + = 0x00000200 # -- Lookahead buffer is not full +I_REPOPGBMAP = 0x00000400 # -- The gbmap is not full +I_COMPACTMETA = 0x00000800 # -- Filesystem may have uncompacted metadata +I_CKMETA = 0x00001000 # -- Metadata checksums not checked recently +I_CKDATA = 0x00002000 # -- Data checksums not checked recently - ('i_INMODE', 0x00030000, "Btree commit mode" ), - ('^_INMTREE', 0x00010000, "Committing to mtree" ), - ('^_INGBMAP', 0x00020000, "Committing to gbmap" ), +I_GBMAP = 0x01000000 # -- Global on-disk block-map in use - # Traversal flags - ('T_MODE', 1, "The traversal's access mode" ), - ('^_RDWR', 0, "Open traversal as read and write" ), - ('^_RDONLY', 1, "Open traversal as read only" ), - ('T_MTREEONLY', 0x00000002, "Only traverse the mtree" ), - ('T_MKCONSISTENT', - 0x00000100, "Make the filesystem consistent" ), - ('T_REPOPLOOKAHEAD', - 0x00000200, "Repopulate lookahead buffer" ), - ('T_REPOPGBMAP', 0x00000400, "Repopulate the gbmap" ), - ('T_COMPACTMETA', 0x00000800, "Compact metadata logs" ), - ('T_CKMETA', 0x00001000, "Check metadata checksums" ), - ('T_CKDATA', 0x00002000, "Check metadata + data checksums" ), +i_INMODE = 0x00030000 # im Btree commit mode +i_INMTREE = 0x00010000 # i^ Committing to mtree +i_INGBMAP = 0x00020000 # i^ Committing to gbmap - ('t_TYPE', 0xf0000000, "The traversal's type" ), - ('^_REG', 0x10000000, "Type = regular-file" ), - ('^_DIR', 0x20000000, "Type = directory" ), - ('^_STICKYNOTE', 0x30000000, "Type = stickynote" ), - ('^_BOOKMARK', 0x40000000, "Type = bookmark" ), - ('^_ORPHAN', 0x50000000, "Type = orphan" ), - ('^_TRAVERSAL', 0x60000000, "Type = traversal" ), - ('^_UNKNOWN', 0x70000000, "Type = unknown" ), - ('t_TSTATE', 0x000f0000, "The current traversal state" ), - ('^_MROOTANCHOR', - 0x00000000, "Tstate = mroot-anchor" ), - ('^_MROOTCHAIN', 0x00010000, "Tstate = mroot-chain" ), - ('^_MTREE', 0x00020000, "Tstate = mtree" ), - ('^_MDIRS', 0x00030000, "Tstate = mtree-mdirs" ), - ('^_MDIR', 0x00040000, "Tstate = mdir" ), - ('^_BTREE', 0x00050000, "Tstate = btree" ), - ('^_HANDLES', 0x00060000, "Tstate = open-mdirs" ), - ('^_HBTREE', 0x00070000, "Tstate = open-btree" ), - ('^_GBMAP', 0x00080000, "Tstate = gbmap" ), - ('^_GBMAP_P', 0x00090000, "Tstate = gbmap_p" ), - ('^_DONE', 0x000a0000, "Tstate = done" ), - ('t_BTYPE', 0x00f00000, "The current block type" ), - ('^_MDIR', 0x00100000, "Btype = mdir" ), - ('^_BTREE', 0x00200000, "Btype = btree" ), - ('^_DATA', 0x00300000, "Btype = data" ), - ('t_ZOMBIE', 0x08000000, "File has been removed" ), - ('t_DIRTY', 0x04000000, "Filesystem ckpointed outside traversal" ), - ('t_CKPOINTED', 0x02000000, "Filesystem ckpointed during traversal" ), +# Traversal flags +T_MODE = 1 # -m The traversal's access mode +T_RDWR = 0 # -^ Open traversal as read and write +T_RDONLY = 1 # -^ Open traversal as read only +T_MTREEONLY = 0x00000002 # -- Only traverse the mtree +T_MKCONSISTENT = 0x00000100 # -- Make the filesystem consistent +T_REPOPLOOKAHEAD \ + = 0x00000200 # -- Repopulate lookahead buffer +T_REPOPGBMAP = 0x00000400 # -- Repopulate the gbmap +T_COMPACTMETA = 0x00000800 # -- Compact metadata logs +T_CKMETA = 0x00001000 # -- Check metadata checksums +T_CKDATA = 0x00002000 # -- Check metadata + data checksums - # Block allocator flags - ('alloc_ERASE', 0x00000001, "Please erase the block" ), +t_TYPE = 0xf0000000 # im The traversal's type +t_REG = 0x10000000 # i^ Type = regular-file +t_DIR = 0x20000000 # i^ Type = directory +t_STICKYNOTE = 0x30000000 # i^ Type = stickynote +t_BOOKMARK = 0x40000000 # i^ Type = bookmark +t_ORPHAN = 0x50000000 # i^ Type = orphan +t_TRAVERSAL = 0x60000000 # i^ Type = traversal +t_UNKNOWN = 0x70000000 # i^ Type = unknown +t_TSTATE = 0x000f0000 # im The current traversal state +t_MROOTANCHOR = 0x00000000 # i^ Tstate = mroot-anchor +t_MROOTCHAIN = 0x00010000 # i^ Tstate = mroot-chain +t_MTREE = 0x00020000 # i^ Tstate = mtree +t_MDIRS = 0x00030000 # i^ Tstate = mtree-mdirs +t_MDIR = 0x00040000 # i^ Tstate = mdir +t_BTREE = 0x00050000 # i^ Tstate = btree +t_HANDLES = 0x00060000 # i^ Tstate = open-mdirs +t_HBTREE = 0x00070000 # i^ Tstate = open-btree +t_GBMAP = 0x00080000 # i^ Tstate = gbmap +t_GBMAP_P = 0x00090000 # i^ Tstate = gbmap_p +t_DONE = 0x000a0000 # i^ Tstate = done +t_BTYPE = 0x00f00000 # im The current block type +t_MDIR = 0x00100000 # i^ Btype = mdir +t_BTREE = 0x00200000 # i^ Btype = btree +t_DATA = 0x00300000 # i^ Btype = data +t_ZOMBIE = 0x08000000 # i- File has been removed +t_DIRTY = 0x04000000 # i- Filesystem ckpointed outside traversal +t_CKPOINTED = 0x02000000 # i- Filesystem ckpointed during traversal - # Read-compat flags - ('RCOMPAT_NONSTANDARD', - 0x00000001, "Non-standard filesystem format" ), - ('RCOMPAT_WRONLY', 0x00000002, "Reading is disallowed" ), - ('RCOMPAT_BMOSS', 0x00000010, "Files may use inlined data" ), - ('RCOMPAT_BSPROUT',0x00000020, "Files may use block pointers" ), - ('RCOMPAT_BSHRUB', 0x00000040, "Files may use inlined btrees" ), - ('RCOMPAT_BTREE', 0x00000080, "Files may use btrees" ), - ('RCOMPAT_MMOSS', 0x00000100, "May use an inlined mdir" ), - ('RCOMPAT_MSPROUT',0x00000200, "May use an mdir pointer" ), - ('RCOMPAT_MSHRUB', 0x00000400, "May use an inlined mtree" ), - ('RCOMPAT_MTREE', 0x00000800, "May use an mdir btree" ), - ('RCOMPAT_GRM', 0x00001000, "Global-remove in use" ), - ('rcompat_OVERFLOW', - 0x80000000, "Can't represent all flags" ), +# Block allocator flags +alloc_ERASE = 0x00000001 # i- Please erase the block - # Write-compat flags - ('WCOMPAT_NONSTANDARD', - 0x00000001, "Non-standard filesystem format" ), - ('WCOMPAT_RDONLY', 0x00000002, "Writing is disallowed" ), - ('WCOMPAT_DIR', 0x00000010, "Directory file types in use" ), - ('WCOMPAT_GCKSUM', 0x00001000, "Global-checksum in use" ), - ('WCOMPAT_GBMAP', 0x00002000, "Global on-disk block-map in use" ), - ('wcompat_OVERFLOW', - 0x80000000, "Can't represent all flags" ), +# Read-compat flags +RCOMPAT_NONSTANDARD \ + = 0x00000001 # -- Non-standard filesystem format +RCOMPAT_WRONLY = 0x00000002 # -- Reading is disallowed +RCOMPAT_BMOSS = 0x00000010 # -- Files may use inlined data +RCOMPAT_BSPROUT = 0x00000020 # -- Files may use block pointers +RCOMPAT_BSHRUB = 0x00000040 # -- Files may use inlined btrees +RCOMPAT_BTREE = 0x00000080 # -- Files may use btrees +RCOMPAT_MMOSS = 0x00000100 # -- May use an inlined mdir +RCOMPAT_MSPROUT = 0x00000200 # -- May use an mdir pointer +RCOMPAT_MSHRUB = 0x00000400 # -- May use an inlined mtree +RCOMPAT_MTREE = 0x00000800 # -- May use an mdir btree +RCOMPAT_GRM = 0x00001000 # -- Global-remove in use +rcompat_OVERFLOW \ + = 0x80000000 # i- Can't represent all flags - # Optional-compat flags - ('OCOMPAT_NONSTANDARD', - 0x00000001, "Non-standard filesystem format" ), - ('ocompat_OVERFLOW', - 0x80000000, "Can't represent all flags" ), -] +# Write-compat flags +WCOMPAT_NONSTANDARD \ + = 0x00000001 # -- Non-standard filesystem format +WCOMPAT_RDONLY = 0x00000002 # -- Writing is disallowed +WCOMPAT_DIR = 0x00000010 # -- Directory file types in use +WCOMPAT_GCKSUM = 0x00001000 # -- Global-checksum in use +WCOMPAT_GBMAP = 0x00002000 # -- Global on-disk block-map in use +wcompat_OVERFLOW \ + = 0x80000000 # i- Can't represent all flags + +# Optional-compat flags +OCOMPAT_NONSTANDARD \ + = 0x00000001 # -- Non-standard filesystem format +ocompat_OVERFLOW \ + = 0x80000000 # i- Can't represent all flags + + +# self-parsing prefixes +class Prefix: + def __init__(self, name, aliases, help): + self.name = name + self.aliases = aliases + self.help = help + + def __repr__(self): + return 'Prefix(%r, %r, %r)' % ( + self.name, + self.aliases, + self.help) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __hash__(self): + return hash(self.name) + +@ft.cache +def prefixes(): + # parse our script's source to figure out prefixes + import inspect + import re + prefixes = [] + prefix_pattern = re.compile( + '^(?PPREFIX_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P.*)$') + for line in (inspect.getsource(inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = prefix_pattern.match(line) + if m: + prefixes.append(Prefix( + m.group('name'), + globals()[m.group('name')], + m.group('help'))) + return prefixes + +# self-parsing flags +class Flag: + def __init__(self, name, flag, help, *, + prefix=None, + yes=False, + internal=False, + mask=False, + type=False): + self.name = name + self.flag = flag + self.help = help + self.prefix = prefix + self.yes = yes + self.internal = internal + self.mask = mask + self.type = type + + def __repr__(self): + return 'Flag(%r, %r, %r)' % ( + self.name, + self.flag, + self.help) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __hash__(self): + return hash(self.name) + + def line(self): + return ('LFS3_%s' % self.name, '0x%08x' % self.flag, self.help) + +@ft.cache +def flags(): + # parse our script's source to figure out flags + import inspect + import re + + # limit to known prefixes + prefixes_ = {p.name.split('_', 1)[1].upper(): p for p in prefixes()} + # keep track of last mask + mask_ = None + + flags = [] + flag_pattern = re.compile( + '^(?P(?i:%s)_[^ ]*) ' + '*= *(?P[^#]*?) *' + '#+ (?P[^ ]+) *(?P.*)$' + % '|'.join(prefixes_.keys())) + for line in (inspect.getsource(inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = flag_pattern.match(line) + if m: + flags.append(Flag( + m.group('name'), + globals()[m.group('name')], + m.group('help'), + # associate flags -> prefix + prefix=prefixes_[m.group('name').split('_', 1)[0].upper()], + yes='y' in m.group('mode'), + internal='i' in m.group('mode'), + mask='m' in m.group('mode'), + # associate types -> mask + type=mask_ if '^' in m.group('mode') else False)) + + # keep track of last mask + if flags[-1].mask: + mask_ = flags[-1] + + return flags def main(flags, *, list=False, all=False, - filter=[]): + prefixes=[]): import builtins list_, list = list, builtins.list all_, all = all, builtins.all - filter_, filter = filter, builtins.filter - # filter by prefix if there are any filters - filter__ = set(filter_) - flags__ = [] - types__ = co.defaultdict(lambda: set()) - for n, f, h in FLAGS: - p, n = n.split('_', 1) - if p == '^': - p = last_p - t = last_t - types__[p].add(t) - else: - t = None - last_p = p - last_t = f + # find flags + flags__ = globals()['flags']() - if not filter__ or p.upper() in filter__: - flags__.append((p, t, n, f, h)) + # filter by prefixes if there are any prefixes + if prefixes: + prefixes = set(prefixes) + flags__ = [f for f in flags__ if f.prefix in prefixes] lines = [] # list all known flags if list_: - for p, t, n, f, h in flags__: - if not all_ and (t is not None or p[0].islower()): + for f in flags__: + if not all_ and (f.internal or f.type): continue - lines.append(('LFS3_%s_%s' % (p, n), '0x%08x' % f, h)) + lines.append(f.line()) # find flags by name or value else: for f_ in flags: found = False # find by LFS3_+prefix+_+name - for p, t, n, f, h in flags__: - if 'LFS3_%s_%s' % (p, n) == f_.upper(): - lines.append(('LFS3_%s_%s' % (p, n), '0x%08x' % f, h)) + for f in flags__: + if 'LFS3_%s' % f.name.upper() == f_.upper(): + lines.append(f.line()) found = True if found: continue # find by prefix+_+name - for p, t, n, f, h in flags__: - if '%s_%s' % (p, n) == f_.upper(): - lines.append(('LFS3_%s_%s' % (p, n), '0x%08x' % f, h)) + for f in flags__: + if '%s' % f.name.upper() == f_.upper(): + lines.append(f.line()) found = True if found: continue # find by name - for p, t, n, f, h in flags__: - if n == f_.upper(): - lines.append(('LFS3_%s_%s' % (p, n), '0x%08x' % f, h)) + for f in flags__: + if f.name.split('_', 1)[1].upper() == f_.upper(): + lines.append(f.line()) found = True if found: continue @@ -274,18 +378,18 @@ def main(flags, *, try: f__ = int(f_, 0) f___ = f__ - for p, t, n, f, h in flags__: + for f in flags__: # ignore type masks here - if t is None and f in types__[p]: + if f.mask: continue # matches flag? - if t is None and (f__ & f) == f: - lines.append(('LFS3_%s_%s' % (p, n), '0x%08x' % f, h)) - f___ &= ~f + if not f.type and (f__ & f.flag) == f.flag: + lines.append(f.line()) + f___ &= ~f.flag # matches type? - elif t is not None and (f__ & t) == f: - lines.append(('LFS3_%s_%s' % (p, n), '0x%08x' % f, h)) - f___ &= ~t + elif f.type and (f__ & f.type.flag) == f.flag: + lines.append(f.line()) + f___ &= ~f.type.flag if f___: lines.append(('?', '0x%08x' % f___, 'Unknown flags')) except ValueError: @@ -323,19 +427,19 @@ if __name__ == "__main__": '-a', '--all', action='store_true', help="Also show internal flags and types.") - class AppendFilter(argparse.Action): + class AppendPrefix(argparse.Action): def __init__(self, nargs=None, **kwargs): super().__init__(nargs=0, **kwargs) def __call__(self, parser, namespace, value, option): - if getattr(namespace, 'filter', None) is None: - namespace.filter = [] - namespace.filter.append(self.const) - for flag, prefix, help in FILTERS: + if getattr(namespace, 'prefixes', None) is None: + namespace.prefixes = [] + namespace.prefixes.append(self.const) + for p in prefixes(): parser.add_argument( - *([flag] if isinstance(flag, str) else flag), - action=AppendFilter, - const=prefix, - help=help) + *p.aliases, + action=AppendPrefix, + const=p, + help=p.help+'.') sys.exit(main(**{k: v for k, v in vars(parser.parse_intermixed_args()).items() if v is not None}))