@@ -408,6 +408,54 @@ def decode_unicode_escape(value):
408408if (__version_info__ [3 ] is None ):
409409 __version__ = str (__version_info__ [0 ]) + "." + str (__version_info__ [1 ]) + "." + str (__version_info__ [2 ])
410410
411+ # ===== Module-level type code table & helpers (reuse anywhere) =====
412+
413+ FT = {
414+ "FILE" : 0 ,
415+ "HARDLINK" : 1 ,
416+ "SYMLINK" : 2 ,
417+ "CHAR" : 3 ,
418+ "BLOCK" : 4 ,
419+ "DIR" : 5 ,
420+ "FIFO" : 6 ,
421+ "FILE_ALT" : 7 , # treated like regular file
422+ "SOCK" : 8 ,
423+ "DOOR" : 9 ,
424+ "PORT" : 10 ,
425+ "WHT" : 11 ,
426+ "JUNCTION" : 13 ,
427+ }
428+
429+ # Base category for each concrete ftype (no unions here).
430+ BASE_CATEGORY_BY_CODE = {
431+ 0 : "files" ,
432+ 7 : "files" ,
433+ 1 : "hardlinks" ,
434+ 2 : "symlinks" ,
435+ 3 : "character" ,
436+ 4 : "block" ,
437+ 5 : "directories" ,
438+ 6 : "fifo" ,
439+ 8 : "sockets" ,
440+ 9 : "doors" ,
441+ 10 : "ports" ,
442+ 11 : "whiteouts" ,
443+ 13 : "junctions" ,
444+ }
445+
446+ # Union categories defined by which base codes should populate them.
447+ UNION_RULES = [
448+ ("links" , set ([FT ["HARDLINK" ], FT ["SYMLINK" ]])),
449+ ("devices" , set ([FT ["CHAR" ], FT ["BLOCK" ]])),
450+ ]
451+
452+ # Deterministic category order (handy for consistent output/printing).
453+ CATEGORY_ORDER = [
454+ "files" , "hardlinks" , "symlinks" , "character" , "block" ,
455+ "directories" , "fifo" , "sockets" , "doors" , "ports" ,
456+ "whiteouts" , "junctions" , "links" , "devices"
457+ ]
458+
411459# Robust bitness detection
412460# Works on Py2 & Py3, all platforms
413461
@@ -7736,72 +7784,122 @@ def ListDirToArray(infiles, dirlistfromtxt=False, fmttype=__file_format_default_
77367784 return listarrayfiles
77377785
77387786
7787+ # ===== Function (keeps inarray schema; returns entries + indexes) =====
7788+
77397789def FoxFileArrayToArrayIndex (inarray , returnfp = False ):
7740- if (isinstance (inarray , dict )):
7741- listarrayfiles = inarray
7742- else :
7790+ """
7791+ Build a bidirectional index over an archive listing while preserving the
7792+ input 'inarray' as-is. Python 2/3 compatible, no external deps.
7793+
7794+ Input (unchanged contract):
7795+ inarray: dict with at least:
7796+ - 'ffilelist': list of dicts: {'fname': <str>, 'fid': <any>, 'ftype': <int>}
7797+ - 'fnumfiles': int (expected count)
7798+ - optional 'fp': any (passed through if returnfp=True)
7799+
7800+ Output structure:
7801+ {
7802+ 'list': inarray, # alias to original input (not copied)
7803+ 'fp': inarray.get('fp') or None,
7804+ 'entries': { fid: {'name': fname, 'type': ftype} },
7805+ 'indexes': {
7806+ 'by_name': { fname: fid },
7807+ 'by_type': {
7808+ <category>: {
7809+ 'by_name': { fname: fid },
7810+ 'by_id': { fid: fname },
7811+ 'count': <int>
7812+ }, ...
7813+ }
7814+ },
7815+ 'counts': {
7816+ 'total': <int>,
7817+ 'by_type': { <category>: <int>, ... }
7818+ },
7819+ 'unknown_types': { <ftype_int>: [fname, ...] }
7820+ }
7821+ """
7822+ if not isinstance (inarray , dict ):
77437823 return False
7744- if ( not listarrayfiles ) :
7824+ if not inarray :
77457825 return False
7746- outarray = {'list' : listarrayfiles , 'filetoid' : {}, 'idtofile' : {}, 'filetypes' : {'directories' : {'filetoid' : {}, 'idtofile' : {}}, 'files' : {'filetoid' : {}, 'idtofile' : {}}, 'links' : {'filetoid' : {}, 'idtofile' : {}}, 'symlinks' : {'filetoid' : {
7747- }, 'idtofile' : {}}, 'hardlinks' : {'filetoid' : {}, 'idtofile' : {}}, 'character' : {'filetoid' : {}, 'idtofile' : {}}, 'block' : {'filetoid' : {}, 'idtofile' : {}}, 'fifo' : {'filetoid' : {}, 'idtofile' : {}}, 'devices' : {'filetoid' : {}, 'idtofile' : {}}}}
7748- if (returnfp ):
7749- outarray .update ({'fp' : listarrayfiles ['fp' ]})
7750- else :
7751- outarray .update ({'fp' : None })
7752- lenlist = len (listarrayfiles ['ffilelist' ])
7753- lcfi = 0
7754- lcfx = int (listarrayfiles ['fnumfiles' ])
7755- if (lenlist > listarrayfiles ['fnumfiles' ] or lenlist < listarrayfiles ['fnumfiles' ]):
7756- lcfx = int (lenlist )
7757- else :
7758- lcfx = int (listarrayfiles ['fnumfiles' ])
7759- while (lcfi < lcfx ):
7760- filetoidarray = {listarrayfiles ['ffilelist' ][lcfi ]
7761- ['fname' ]: listarrayfiles ['ffilelist' ][lcfi ]['fid' ]}
7762- idtofilearray = {listarrayfiles ['ffilelist' ][lcfi ]
7763- ['fid' ]: listarrayfiles ['ffilelist' ][lcfi ]['fname' ]}
7764- outarray ['filetoid' ].update (filetoidarray )
7765- outarray ['idtofile' ].update (idtofilearray )
7766- if (listarrayfiles ['ffilelist' ][lcfi ]['ftype' ] == 0 or listarrayfiles ['ffilelist' ][lcfi ]['ftype' ] == 7 ):
7767- outarray ['filetypes' ]['files' ]['filetoid' ].update (filetoidarray )
7768- outarray ['filetypes' ]['files' ]['idtofile' ].update (idtofilearray )
7769- if (listarrayfiles ['ffilelist' ][lcfi ]['ftype' ] == 1 ):
7770- outarray ['filetypes' ]['hardlinks' ]['filetoid' ].update (
7771- filetoidarray )
7772- outarray ['filetypes' ]['hardlinks' ]['idtofile' ].update (
7773- idtofilearray )
7774- outarray ['filetypes' ]['links' ]['filetoid' ].update (filetoidarray )
7775- outarray ['filetypes' ]['links' ]['idtofile' ].update (idtofilearray )
7776- if (listarrayfiles ['ffilelist' ][lcfi ]['ftype' ] == 2 ):
7777- outarray ['filetypes' ]['symlinks' ]['filetoid' ].update (filetoidarray )
7778- outarray ['filetypes' ]['symlinks' ]['idtofile' ].update (idtofilearray )
7779- outarray ['filetypes' ]['links' ]['filetoid' ].update (filetoidarray )
7780- outarray ['filetypes' ]['links' ]['idtofile' ].update (idtofilearray )
7781- if (listarrayfiles ['ffilelist' ][lcfi ]['ftype' ] == 3 ):
7782- outarray ['filetypes' ]['character' ]['filetoid' ].update (
7783- filetoidarray )
7784- outarray ['filetypes' ]['character' ]['idtofile' ].update (
7785- idtofilearray )
7786- outarray ['filetypes' ]['devices' ]['filetoid' ].update (filetoidarray )
7787- outarray ['filetypes' ]['devices' ]['idtofile' ].update (idtofilearray )
7788- if (listarrayfiles ['ffilelist' ][lcfi ]['ftype' ] == 4 ):
7789- outarray ['filetypes' ]['block' ]['filetoid' ].update (filetoidarray )
7790- outarray ['filetypes' ]['block' ]['idtofile' ].update (idtofilearray )
7791- outarray ['filetypes' ]['devices' ]['filetoid' ].update (filetoidarray )
7792- outarray ['filetypes' ]['devices' ]['idtofile' ].update (idtofilearray )
7793- if (listarrayfiles ['ffilelist' ][lcfi ]['ftype' ] == 5 ):
7794- outarray ['filetypes' ]['directories' ]['filetoid' ].update (
7795- filetoidarray )
7796- outarray ['filetypes' ]['directories' ]['idtofile' ].update (
7797- idtofilearray )
7798- if (listarrayfiles ['ffilelist' ][lcfi ]['ftype' ] == 6 ):
7799- outarray ['filetypes' ]['symlinks' ]['filetoid' ].update (filetoidarray )
7800- outarray ['filetypes' ]['symlinks' ]['idtofile' ].update (idtofilearray )
7801- outarray ['filetypes' ]['devices' ]['filetoid' ].update (filetoidarray )
7802- outarray ['filetypes' ]['devices' ]['idtofile' ].update (idtofilearray )
7803- lcfi = lcfi + 1
7804- return outarray
7826+
7827+ # Buckets for categories
7828+ def _bucket ():
7829+ return {"by_name" : {}, "by_id" : {}, "count" : 0 }
7830+
7831+ by_type = {}
7832+ for cat in CATEGORY_ORDER :
7833+ by_type [cat ] = _bucket ()
7834+
7835+ out = {
7836+ "list" : inarray ,
7837+ "fp" : inarray .get ("fp" ) if returnfp else None ,
7838+ "entries" : {},
7839+ "indexes" : {
7840+ "by_name" : {},
7841+ "by_type" : by_type ,
7842+ },
7843+ "counts" : {"total" : 0 , "by_type" : {}},
7844+ "unknown_types" : {},
7845+ }
7846+
7847+ ffilelist = inarray .get ("ffilelist" ) or []
7848+ try :
7849+ fnumfiles = int (inarray .get ("fnumfiles" , len (ffilelist )))
7850+ except Exception :
7851+ fnumfiles = len (ffilelist )
7852+
7853+ # Process only what's present
7854+ total = min (len (ffilelist ), fnumfiles )
7855+
7856+ def _add (cat , name , fid ):
7857+ b = by_type [cat ]
7858+ b ["by_name" ][name ] = fid
7859+ b ["by_id" ][fid ] = name
7860+ # Count is number of unique names in this category
7861+ b ["count" ] = len (b ["by_name" ])
7862+
7863+ i = 0
7864+ while i < total :
7865+ e = ffilelist [i ]
7866+ name = e .get ("fname" )
7867+ fid = e .get ("fid" )
7868+ t = e .get ("ftype" )
7869+
7870+ if name is None or fid is None or t is None :
7871+ i += 1
7872+ continue
7873+
7874+ # Store canonical entry once, keyed by fid
7875+ out ["entries" ][fid ] = {"name" : name , "type" : t }
7876+
7877+ # Global reverse index for fast name -> id
7878+ out ["indexes" ]["by_name" ][name ] = fid
7879+
7880+ # Base category
7881+ base_cat = BASE_CATEGORY_BY_CODE .get (t )
7882+ if base_cat is not None :
7883+ _add (base_cat , name , fid )
7884+ else :
7885+ # Track unknown codes for visibility/forward-compat
7886+ lst = out ["unknown_types" ].setdefault (t , [])
7887+ if name not in lst :
7888+ lst .append (name )
7889+
7890+ # Union categories
7891+ for union_name , code_set in UNION_RULES :
7892+ if t in code_set :
7893+ _add (union_name , name , fid )
7894+
7895+ i += 1
7896+
7897+ # Counts
7898+ out ["counts" ]["total" ] = total
7899+ for cat in CATEGORY_ORDER :
7900+ out ["counts" ]["by_type" ][cat ] = by_type [cat ]["count" ]
7901+
7902+ return out
78057903
78067904
78077905def RePackFoxFile (infile , outfile , fmttype = "auto" , compression = "auto" , compresswholefile = True , compressionlevel = None , compressionuselist = compressionlistalt , followlink = False , filestart = 0 , seekstart = 0 , seekend = 0 , checksumtype = ["crc32" , "crc32" , "crc32" , "crc32" ], skipchecksum = False , extradata = [], jsondata = {}, formatspecs = __file_format_dict__ , seektoend = False , verbose = False , returnfp = False ):
0 commit comments