2018-02-12 21:48:46 +00:00
import argparse
import collections
import enum
2019-09-15 13:38:01 +00:00
import itertools
2018-02-12 21:48:46 +00:00
import sys
import textwrap
import typing
2019-07-14 00:16:49 +00:00
from . import __version__ , api , compress
2018-02-12 21:48:46 +00:00
2019-09-03 00:10:04 +00:00
# The encoding to use when rendering bytes as text (in four-char codes, strings, hex dumps, etc.) or reading a quoted byte string (from the command line).
_TEXT_ENCODING = " MacRoman "
2018-02-12 21:48:46 +00:00
# Translation table to replace ASCII non-printable characters with periods.
_TRANSLATE_NONPRINTABLES = { k : " . " for k in [ * range ( 0x20 ) , 0x7f ] }
_REZ_ATTR_NAMES = {
api . ResourceAttrs . resSysRef : None , # "Illegal or reserved attribute"
api . ResourceAttrs . resSysHeap : " sysheap " ,
api . ResourceAttrs . resPurgeable : " purgeable " ,
api . ResourceAttrs . resLocked : " locked " ,
api . ResourceAttrs . resProtected : " protected " ,
api . ResourceAttrs . resPreload : " preload " ,
api . ResourceAttrs . resChanged : None , # "Illegal or reserved attribute"
api . ResourceAttrs . resCompressed : None , # "Extended Header resource attribute"
}
2019-09-29 14:06:06 +00:00
F = typing . TypeVar ( " F " , bound = enum . Flag )
2019-09-30 17:01:31 +00:00
def decompose_flags ( value : F ) - > typing . Sequence [ F ] :
2018-02-12 21:48:46 +00:00
""" Decompose an enum.Flags instance into separate enum constants. """
return [ bit for bit in type ( value ) if bit in value ]
2019-09-30 17:01:31 +00:00
def is_printable ( char : str ) - > bool :
2019-09-03 00:10:04 +00:00
""" Determine whether a character is printable for our purposes.
We mainly use Python ' s definition of printable (i. e. everything that Unicode does not consider a separator or " other " character). However, we also treat U+F8FF as printable, which is the private use codepoint used for the Apple logo character.
"""
return char . isprintable ( ) or char == " \uf8ff "
2019-09-30 17:01:31 +00:00
def bytes_unescape ( string : str ) - > bytes :
2019-09-03 00:10:04 +00:00
""" Convert a string containing text (in _TEXT_ENCODING) and hex escapes to a bytestring.
2018-02-12 21:48:46 +00:00
( We implement our own unescaping mechanism here to not depend on any of Python ' s string/bytes escape syntax.)
"""
2019-09-29 14:06:06 +00:00
out : typing . List [ int ] = [ ]
2018-02-12 21:48:46 +00:00
it = iter ( string )
for char in it :
if char == " \\ " :
try :
esc = next ( it )
if esc in " \\ \' \" " :
2019-09-29 14:06:06 +00:00
out . extend ( esc . encode ( _TEXT_ENCODING ) )
2018-02-12 21:48:46 +00:00
elif esc == " x " :
x1 , x2 = next ( it ) , next ( it )
out . append ( int ( x1 + x2 , 16 ) )
else :
raise ValueError ( f " Unknown escape character: { esc } " )
except StopIteration :
raise ValueError ( " End of string in escape sequence " )
else :
2019-09-03 00:10:04 +00:00
out . extend ( char . encode ( _TEXT_ENCODING ) )
2018-02-12 21:48:46 +00:00
return bytes ( out )
2019-09-30 17:01:31 +00:00
def bytes_escape ( bs : bytes , * , quote : typing . Optional [ str ] = None ) - > str :
2019-09-03 00:10:04 +00:00
""" Convert a bytestring to a string (using _TEXT_ENCODING), with non-printable characters hex-escaped.
2018-02-12 21:48:46 +00:00
( We implement our own escaping mechanism here to not depend on Python ' s str or bytes repr.)
"""
out = [ ]
2019-09-03 00:10:04 +00:00
for byte , char in zip ( bs , bs . decode ( _TEXT_ENCODING ) ) :
if char in { quote , " \\ " } :
out . append ( f " \\ { char } " )
2019-09-30 17:01:31 +00:00
elif is_printable ( char ) :
2019-09-03 00:10:04 +00:00
out . append ( char )
2018-02-12 21:48:46 +00:00
else :
out . append ( f " \\ x { byte : 02x } " )
return " " . join ( out )
2019-09-30 17:01:31 +00:00
def filter_resources ( rf : api . ResourceFile , filters : typing . Sequence [ str ] ) - > typing . List [ api . Resource ] :
2019-09-29 14:06:06 +00:00
matching : typing . MutableMapping [ typing . Tuple [ bytes , int ] , api . Resource ] = collections . OrderedDict ( )
2018-02-12 21:48:46 +00:00
for filter in filters :
if len ( filter ) == 4 :
try :
resources = rf [ filter . encode ( " ascii " ) ]
except KeyError :
continue
for res in resources . values ( ) :
2019-09-15 13:56:03 +00:00
matching [ res . type , res . id ] = res
2018-02-12 21:48:46 +00:00
elif filter [ 0 ] == filter [ - 1 ] == " ' " :
try :
2019-09-30 17:01:31 +00:00
resources = rf [ bytes_unescape ( filter [ 1 : - 1 ] ) ]
2018-02-12 21:48:46 +00:00
except KeyError :
continue
for res in resources . values ( ) :
2019-09-15 13:56:03 +00:00
matching [ res . type , res . id ] = res
2018-02-12 21:48:46 +00:00
else :
pos = filter . find ( " ' " , 1 )
if pos == - 1 :
raise ValueError ( f " Invalid filter { filter !r} : Resource type must be single-quoted " )
elif filter [ pos + 1 ] != " " :
raise ValueError ( f " Invalid filter { filter !r} : Resource type and ID must be separated by a space " )
2019-09-29 14:06:06 +00:00
restype_str , resid_str = filter [ : pos + 1 ] , filter [ pos + 2 : ]
2018-02-12 21:48:46 +00:00
2019-09-29 14:06:06 +00:00
if not restype_str [ 0 ] == restype_str [ - 1 ] == " ' " :
2018-02-12 21:48:46 +00:00
raise ValueError (
2019-09-29 14:06:06 +00:00
f " Invalid filter { filter !r} : Resource type is not a single-quoted type identifier: { restype_str !r} " )
2019-09-30 17:01:31 +00:00
restype = bytes_unescape ( restype_str [ 1 : - 1 ] )
2018-02-12 21:48:46 +00:00
if len ( restype ) != 4 :
raise ValueError (
f " Invalid filter { filter !r} : Type identifier must be 4 bytes after replacing escapes, got { len ( restype ) } bytes: { restype !r} " )
2019-09-29 14:06:06 +00:00
if resid_str [ 0 ] != " ( " or resid_str [ - 1 ] != " ) " :
2018-02-12 21:48:46 +00:00
raise ValueError ( f " Invalid filter { filter !r} : Resource ID must be parenthesized " )
2019-09-29 14:06:06 +00:00
resid_str = resid_str [ 1 : - 1 ]
2018-02-12 21:48:46 +00:00
try :
resources = rf [ restype ]
except KeyError :
continue
2019-09-29 14:06:06 +00:00
if resid_str [ 0 ] == resid_str [ - 1 ] == ' " ' :
2019-09-30 17:01:31 +00:00
name = bytes_unescape ( resid_str [ 1 : - 1 ] )
2018-02-12 21:48:46 +00:00
for res in resources . values ( ) :
if res . name == name :
2019-09-15 13:56:03 +00:00
matching [ res . type , res . id ] = res
2018-02-12 21:48:46 +00:00
break
2019-09-29 14:06:06 +00:00
elif " : " in resid_str :
if resid_str . count ( " : " ) > 1 :
raise ValueError ( f " Invalid filter { filter !r} : Too many colons in ID range expression: { resid_str !r} " )
start_str , end_str = resid_str . split ( " : " )
start , end = int ( start_str ) , int ( end_str )
2018-02-12 21:48:46 +00:00
for res in resources . values ( ) :
2019-09-15 13:56:03 +00:00
if start < = res . id < = end :
matching [ res . type , res . id ] = res
2018-02-12 21:48:46 +00:00
else :
2019-09-29 14:06:06 +00:00
resid = int ( resid_str )
2018-02-12 21:48:46 +00:00
try :
res = resources [ resid ]
except KeyError :
continue
2019-09-15 13:56:03 +00:00
matching [ res . type , res . id ] = res
2018-02-12 21:48:46 +00:00
return list ( matching . values ( ) )
2019-09-30 17:01:31 +00:00
def hexdump ( data : bytes ) - > None :
2019-09-13 08:40:03 +00:00
last_line = None
asterisk_shown = False
2018-02-12 21:48:46 +00:00
for i in range ( 0 , len ( data ) , 16 ) :
line = data [ i : i + 16 ]
2019-09-13 08:40:03 +00:00
# If the same 16-byte lines appear multiple times, print only the first one, and replace all further lines with a single line with an asterisk.
# This is unambiguous - to find out how many lines were collapsed this way, the user can compare the addresses of the lines before and after the asterisk.
if line == last_line :
if not asterisk_shown :
print ( " * " )
asterisk_shown = True
else :
2019-09-13 08:51:27 +00:00
line_hex_left = " " . join ( f " { byte : 02x } " for byte in line [ : 8 ] )
line_hex_right = " " . join ( f " { byte : 02x } " for byte in line [ 8 : ] )
2019-09-13 08:40:03 +00:00
line_char = line . decode ( _TEXT_ENCODING ) . translate ( _TRANSLATE_NONPRINTABLES )
2019-09-13 08:51:27 +00:00
print ( f " { i : 08x } { line_hex_left : < { 8 * 2 + 7 } } { line_hex_right : < { 8 * 2 + 7 } } | { line_char } | " )
2019-09-13 08:40:03 +00:00
asterisk_shown = False
last_line = line
2018-02-12 21:48:46 +00:00
if data :
print ( f " { len ( data ) : 08x } " )
2019-09-30 17:01:31 +00:00
def raw_hexdump ( data : bytes ) - > None :
2018-02-12 21:48:46 +00:00
for i in range ( 0 , len ( data ) , 16 ) :
print ( " " . join ( f " { byte : 02x } " for byte in data [ i : i + 16 ] ) )
2019-09-30 17:01:31 +00:00
def translate_text ( data : bytes ) - > str :
2019-09-13 12:51:16 +00:00
return data . decode ( _TEXT_ENCODING ) . replace ( " \r " , " \n " )
2019-09-30 17:01:31 +00:00
def describe_resource ( res : api . Resource , * , include_type : bool , decompress : bool ) - > str :
2019-09-15 13:56:03 +00:00
id_desc_parts = [ f " { res . id } " ]
2019-09-02 23:27:41 +00:00
if res . name is not None :
2019-09-30 17:01:31 +00:00
name = bytes_escape ( res . name , quote = ' " ' )
2019-09-02 23:27:41 +00:00
id_desc_parts . append ( f ' " { name } " ' )
id_desc = " , " . join ( id_desc_parts )
content_desc_parts = [ ]
if decompress and api . ResourceAttrs . resCompressed in res . attributes :
try :
2019-09-23 22:13:23 +00:00
res . compressed_info
2019-09-02 23:27:41 +00:00
except compress . DecompressError :
2019-09-23 22:27:54 +00:00
length_desc = f " unparseable compressed data header ( { res . length_raw } bytes compressed) "
2019-09-02 23:27:41 +00:00
else :
2019-09-29 14:06:06 +00:00
assert res . compressed_info is not None
2019-09-23 22:27:54 +00:00
length_desc = f " { res . length } bytes ( { res . length_raw } bytes compressed, ' dcmp ' ( { res . compressed_info . dcmp_id } ) format) "
2019-09-02 23:27:41 +00:00
else :
2019-09-29 14:06:06 +00:00
assert res . compressed_info is None
2019-09-23 22:13:23 +00:00
length_desc = f " { res . length_raw } bytes "
2019-09-02 23:27:41 +00:00
content_desc_parts . append ( length_desc )
2019-09-30 17:01:31 +00:00
attrs = decompose_flags ( res . attributes )
2019-09-02 23:27:41 +00:00
if attrs :
content_desc_parts . append ( " | " . join ( attr . name for attr in attrs ) )
content_desc = " , " . join ( content_desc_parts )
desc = f " ( { id_desc } ): { content_desc } "
if include_type :
2019-09-30 17:01:31 +00:00
restype = bytes_escape ( res . type , quote = " ' " )
2019-09-02 23:27:41 +00:00
desc = f " ' { restype } ' { desc } "
return desc
2019-09-30 17:01:31 +00:00
def parse_args ( ) - > argparse . Namespace :
2018-02-12 21:48:46 +00:00
ap = argparse . ArgumentParser (
add_help = False ,
fromfile_prefix_chars = " @ " ,
formatter_class = argparse . RawDescriptionHelpFormatter ,
description = textwrap . dedent ( """
Read and display resources from a file ' s resource or data fork.
When specifying resource filters , each one may be of one of the
following forms :
An unquoted type name ( without escapes ) : TYPE
A quoted type name : ' TYPE '
A quoted type name and an ID : ' TYPE ' ( 42 )
A quoted type name and an ID range : ' TYPE ' ( 24 : 42 )
A quoted type name and a resource name : ' TYPE ' ( " foobar " )
When multiple filters are specified , all resources matching any of them
are displayed .
""" ),
)
ap . add_argument ( " --help " , action = " help " , help = " Display this help message and exit " )
ap . add_argument ( " --version " , action = " version " , version = __version__ , help = " Display version information and exit " )
ap . add_argument ( " -a " , " --all " , action = " store_true " , help = " When no filters are given, show all resources in full, instead of an overview " )
ap . add_argument ( " -f " , " --fork " , choices = [ " auto " , " data " , " rsrc " ] , default = " auto " , help = " The fork from which to read the resource data, or auto to guess (default: %(default)s ) " )
2019-07-14 00:16:49 +00:00
ap . add_argument ( " --no-decompress " , action = " store_false " , dest = " decompress " , help = " Do not decompress compressed resources, output compressed resource data as-is " )
2019-09-13 12:51:16 +00:00
ap . add_argument ( " --format " , choices = [ " dump " , " dump-text " , " hex " , " raw " , " derez " ] , default = " dump " , help = " How to output the resources - human-readable info with hex dump (dump) (default), human-readable info with newline-translated data (dump-text), data only as hex (hex), data only as raw bytes (raw), or like DeRez with no resource definitions (derez) " )
2019-09-15 13:38:01 +00:00
ap . add_argument ( " --group " , action = " store " , choices = [ " none " , " type " , " id " ] , default = " type " , help = " Group resources in list view by type or ID, or disable grouping (default: type) " )
2019-09-16 13:25:41 +00:00
ap . add_argument ( " --no-sort " , action = " store_false " , dest = " sort " , help = " Output resources in the order in which they are stored in the file, instead of sorting them by type and ID " )
2018-02-12 21:48:46 +00:00
ap . add_argument ( " --header-system " , action = " store_true " , help = " Output system-reserved header data and nothing else " )
ap . add_argument ( " --header-application " , action = " store_true " , help = " Output application-specific header data and nothing else " )
ap . add_argument ( " file " , help = " The file to read, or - for stdin " )
ap . add_argument ( " filter " , nargs = " * " , help = " One or more filters to select which resources to display, or omit to show an overview of all resources " )
2019-07-13 23:23:08 +00:00
ns = ap . parse_args ( )
2019-09-13 10:36:37 +00:00
return ns
2019-09-30 17:01:31 +00:00
def show_header_data ( data : bytes , * , format : str ) - > None :
2019-09-13 10:36:37 +00:00
if format == " dump " :
2019-09-30 17:01:31 +00:00
hexdump ( data )
2019-09-13 12:51:16 +00:00
elif format == " dump-text " :
2019-09-30 17:01:31 +00:00
print ( translate_text ( data ) )
2019-09-13 10:36:37 +00:00
elif format == " hex " :
2019-09-30 17:01:31 +00:00
raw_hexdump ( data )
2019-09-13 10:36:37 +00:00
elif format == " raw " :
sys . stdout . buffer . write ( data )
elif format == " derez " :
print ( " Cannot output file header data in derez format " , file = sys . stderr )
sys . exit ( 1 )
else :
raise ValueError ( f " Unhandled output format: { format } " )
2019-09-30 17:01:31 +00:00
def show_filtered_resources ( resources : typing . Sequence [ api . Resource ] , format : str , decompress : bool ) - > None :
2019-09-13 10:36:37 +00:00
if not resources :
2019-09-13 12:51:16 +00:00
if format in ( " dump " , " dump-text " ) :
2019-09-13 10:36:37 +00:00
print ( " No resources matched the filter " )
elif format in ( " hex " , " raw " ) :
print ( " No resources matched the filter " , file = sys . stderr )
sys . exit ( 1 )
elif format == " derez " :
print ( " /* No resources matched the filter */ " )
else :
raise AssertionError ( f " Unhandled output format: { format } " )
elif format in ( " hex " , " raw " ) and len ( resources ) != 1 :
print ( f " Format { format } can only output a single resource, but the filter matched { len ( resources ) } resources " , file = sys . stderr )
sys . exit ( 1 )
for res in resources :
if decompress :
data = res . data
else :
data = res . data_raw
2019-09-13 12:51:16 +00:00
if format in ( " dump " , " dump-text " ) :
# Human-readable info and hex or text dump
2019-09-30 17:01:31 +00:00
desc = describe_resource ( res , include_type = True , decompress = decompress )
2019-09-13 10:36:37 +00:00
print ( f " Resource { desc } : " )
2019-09-13 12:51:16 +00:00
if format == " dump " :
2019-09-30 17:01:31 +00:00
hexdump ( data )
2019-09-13 12:51:16 +00:00
elif format == " dump-text " :
2019-09-30 17:01:31 +00:00
print ( translate_text ( data ) )
2019-09-13 12:51:16 +00:00
else :
raise AssertionError ( f " Unhandled format: { format !r} " )
2019-09-13 10:36:37 +00:00
print ( )
elif format == " hex " :
# Data only as hex
2019-09-30 17:01:31 +00:00
raw_hexdump ( data )
2019-09-13 10:36:37 +00:00
elif format == " raw " :
# Data only as raw bytes
sys . stdout . buffer . write ( data )
elif format == " derez " :
# Like DeRez with no resource definitions
2019-09-30 17:01:31 +00:00
attrs = list ( decompose_flags ( res . attributes ) )
2019-09-13 10:36:37 +00:00
if decompress and api . ResourceAttrs . resCompressed in attrs :
attrs . remove ( api . ResourceAttrs . resCompressed )
attrs_comment = " /* was compressed */ "
else :
attrs_comment = " "
2019-09-29 14:06:06 +00:00
attr_descs_with_none = [ _REZ_ATTR_NAMES [ attr ] for attr in attrs ]
if None in attr_descs_with_none :
attr_descs = [ f " $ { res . attributes . value : 02X } " ]
else :
attr_descs = typing . cast ( typing . List [ str ] , attr_descs_with_none )
2019-09-13 10:36:37 +00:00
2019-09-15 13:56:03 +00:00
parts = [ str ( res . id ) ]
2019-09-13 10:36:37 +00:00
if res . name is not None :
2019-09-30 17:01:31 +00:00
name = bytes_escape ( res . name , quote = ' " ' )
2019-09-13 10:36:37 +00:00
parts . append ( f ' " { name } " ' )
parts + = attr_descs
2019-09-30 17:01:31 +00:00
restype = bytes_escape ( res . type , quote = " ' " )
2019-09-13 10:36:37 +00:00
print ( f " data ' { restype } ' ( { ' , ' . join ( parts ) } { attrs_comment } ) {{ " )
for i in range ( 0 , len ( data ) , 16 ) :
# Two-byte grouping is really annoying to implement.
groups = [ ]
for j in range ( 0 , 16 , 2 ) :
if i + j > = len ( data ) :
break
elif i + j + 1 > = len ( data ) :
groups . append ( f " { data [ i + j ] : 02X } " )
else :
groups . append ( f " { data [ i + j ] : 02X } { data [ i + j + 1 ] : 02X } " )
s = f ' $ " { " " . join ( groups ) } " '
comment = " /* " + data [ i : i + 16 ] . decode ( _TEXT_ENCODING ) . translate ( _TRANSLATE_NONPRINTABLES ) + " */ "
print ( f " \t { s : <54s } { comment } " )
print ( " }; " )
print ( )
else :
raise ValueError ( f " Unhandled output format: { format } " )
2019-09-30 17:01:31 +00:00
def list_resource_file ( rf : api . ResourceFile , * , sort : bool , group : str , decompress : bool ) - > None :
2019-09-13 10:36:37 +00:00
if rf . header_system_data != bytes ( len ( rf . header_system_data ) ) :
print ( " Header system data: " )
2019-09-30 17:01:31 +00:00
hexdump ( rf . header_system_data )
2019-09-13 10:36:37 +00:00
if rf . header_application_data != bytes ( len ( rf . header_application_data ) ) :
print ( " Header application data: " )
2019-09-30 17:01:31 +00:00
hexdump ( rf . header_application_data )
2019-09-13 10:36:37 +00:00
2019-09-30 17:01:31 +00:00
attrs = decompose_flags ( rf . file_attributes )
2019-09-13 10:36:37 +00:00
if attrs :
print ( " File attributes: " + " | " . join ( attr . name for attr in attrs ) )
2019-09-15 13:38:01 +00:00
if len ( rf ) == 0 :
print ( " No resources (empty resource file) " )
return
if group == " none " :
2019-09-29 14:06:06 +00:00
all_resources : typing . List [ api . Resource ] = [ ]
2019-09-15 13:38:01 +00:00
for reses in rf . values ( ) :
all_resources . extend ( reses . values ( ) )
if sort :
2019-09-15 13:56:03 +00:00
all_resources . sort ( key = lambda res : ( res . type , res . id ) )
2019-09-15 13:38:01 +00:00
print ( f " { len ( all_resources ) } resources: " )
for res in all_resources :
2019-09-30 17:01:31 +00:00
print ( describe_resource ( res , include_type = True , decompress = decompress ) )
2019-09-15 13:38:01 +00:00
elif group == " type " :
2019-09-13 10:36:37 +00:00
print ( f " { len ( rf ) } resource types: " )
2019-09-29 14:06:06 +00:00
restype_items : typing . Collection [ typing . Tuple [ bytes , typing . Mapping [ int , api . Resource ] ] ] = rf . items ( )
2019-09-13 13:00:56 +00:00
if sort :
restype_items = sorted ( restype_items , key = lambda item : item [ 0 ] )
2019-09-29 14:06:06 +00:00
for typecode , resources_map in restype_items :
2019-09-30 17:01:31 +00:00
restype = bytes_escape ( typecode , quote = " ' " )
2019-09-29 14:06:06 +00:00
print ( f " ' { restype } ' : { len ( resources_map ) } resources: " )
resources_items : typing . Collection [ typing . Tuple [ int , api . Resource ] ] = resources_map . items ( )
2019-09-13 13:00:56 +00:00
if sort :
resources_items = sorted ( resources_items , key = lambda item : item [ 0 ] )
for resid , res in resources_items :
2019-09-30 17:01:31 +00:00
print ( describe_resource ( res , include_type = False , decompress = decompress ) )
2019-09-13 10:36:37 +00:00
print ( )
2019-09-15 13:38:01 +00:00
elif group == " id " :
all_resources = [ ]
for reses in rf . values ( ) :
all_resources . extend ( reses . values ( ) )
2019-09-15 13:56:03 +00:00
all_resources . sort ( key = lambda res : res . id )
resources_by_id = { resid : list ( reses ) for resid , reses in itertools . groupby ( all_resources , key = lambda res : res . id ) }
2019-09-15 13:38:01 +00:00
print ( f " { len ( resources_by_id ) } resource IDs: " )
for resid , resources in resources_by_id . items ( ) :
2019-09-16 12:58:21 +00:00
print ( f " ( { resid } ): { len ( resources ) } resources: " )
2019-09-15 13:38:01 +00:00
if sort :
2019-09-15 13:56:03 +00:00
resources . sort ( key = lambda res : res . type )
2019-09-15 13:38:01 +00:00
for res in resources :
2019-09-30 17:01:31 +00:00
print ( describe_resource ( res , include_type = True , decompress = decompress ) )
2019-09-15 13:38:01 +00:00
print ( )
2019-09-13 10:36:37 +00:00
else :
2019-09-15 13:38:01 +00:00
raise AssertionError ( f " Unhandled group mode: { group !r} " )
2019-09-13 10:36:37 +00:00
2019-09-29 13:32:18 +00:00
def main ( ) - > typing . NoReturn :
2019-09-30 17:01:31 +00:00
ns = parse_args ( )
2018-02-12 21:48:46 +00:00
if ns . file == " - " :
2019-10-16 21:29:20 +00:00
if ns . fork != " auto " :
2018-02-12 21:48:46 +00:00
print ( " Cannot specify an explicit fork when reading from stdin " , file = sys . stderr )
sys . exit ( 1 )
2019-08-25 22:51:10 +00:00
rf = api . ResourceFile ( sys . stdin . buffer )
2018-02-12 21:48:46 +00:00
else :
2019-08-31 18:07:26 +00:00
rf = api . ResourceFile . open ( ns . file , fork = ns . fork )
2018-02-12 21:48:46 +00:00
with rf :
if ns . header_system or ns . header_application :
if ns . header_system :
data = rf . header_system_data
else :
data = rf . header_application_data
2019-09-30 17:01:31 +00:00
show_header_data ( data , format = ns . format )
2018-02-12 21:48:46 +00:00
elif ns . filter or ns . all :
if ns . filter :
2019-09-30 17:01:31 +00:00
resources = filter_resources ( rf , ns . filter )
2018-02-12 21:48:46 +00:00
else :
resources = [ ]
for reses in rf . values ( ) :
resources . extend ( reses . values ( ) )
2019-09-13 13:00:56 +00:00
if ns . sort :
2019-09-15 13:56:03 +00:00
resources . sort ( key = lambda res : ( res . type , res . id ) )
2019-09-13 13:00:56 +00:00
2019-09-30 17:01:31 +00:00
show_filtered_resources ( resources , format = ns . format , decompress = ns . decompress )
2018-02-12 21:48:46 +00:00
else :
2019-09-30 17:01:31 +00:00
list_resource_file ( rf , sort = ns . sort , group = ns . group , decompress = ns . decompress )
2018-02-12 21:48:46 +00:00
sys . exit ( 0 )
if __name__ == " __main__ " :
2019-07-13 23:23:08 +00:00
sys . exit ( main ( ) )