biffh.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. # -*- coding: utf-8 -*-
  2. # Portions copyright © 2005-2010 Stephen John Machin, Lingfo Pty Ltd
  3. # This module is part of the xlrd package, which is released under a
  4. # BSD-style licence.
  5. from __future__ import print_function
  6. import sys
  7. from struct import unpack
  8. from .timemachine import *
  9. DEBUG = 0
  10. class XLRDError(Exception):
  11. """
  12. An exception indicating problems reading data from an Excel file.
  13. """
  14. class BaseObject(object):
  15. """
  16. Parent of almost all other classes in the package. Defines a common
  17. :meth:`dump` method for debugging.
  18. """
  19. _repr_these = []
  20. def dump(self, f=None, header=None, footer=None, indent=0):
  21. """
  22. :param f: open file object, to which the dump is written
  23. :param header: text to write before the dump
  24. :param footer: text to write after the dump
  25. :param indent: number of leading spaces (for recursive calls)
  26. """
  27. if f is None:
  28. f = sys.stderr
  29. if hasattr(self, "__slots__"):
  30. alist = []
  31. for attr in self.__slots__:
  32. alist.append((attr, getattr(self, attr)))
  33. else:
  34. alist = self.__dict__.items()
  35. alist = sorted(alist)
  36. pad = " " * indent
  37. if header is not None: print(header, file=f)
  38. list_type = type([])
  39. dict_type = type({})
  40. for attr, value in alist:
  41. if getattr(value, 'dump', None) and attr != 'book':
  42. value.dump(f,
  43. header="%s%s (%s object):" % (pad, attr, value.__class__.__name__),
  44. indent=indent+4)
  45. elif (attr not in self._repr_these and
  46. (isinstance(value, list_type) or isinstance(value, dict_type))):
  47. print("%s%s: %s, len = %d" % (pad, attr, type(value), len(value)), file=f)
  48. else:
  49. fprintf(f, "%s%s: %r\n", pad, attr, value)
  50. if footer is not None: print(footer, file=f)
  51. FUN, FDT, FNU, FGE, FTX = range(5) # unknown, date, number, general, text
  52. DATEFORMAT = FDT
  53. NUMBERFORMAT = FNU
  54. (
  55. XL_CELL_EMPTY,
  56. XL_CELL_TEXT,
  57. XL_CELL_NUMBER,
  58. XL_CELL_DATE,
  59. XL_CELL_BOOLEAN,
  60. XL_CELL_ERROR,
  61. XL_CELL_BLANK, # for use in debugging, gathering stats, etc
  62. ) = range(7)
  63. biff_text_from_num = {
  64. 0: "(not BIFF)",
  65. 20: "2.0",
  66. 21: "2.1",
  67. 30: "3",
  68. 40: "4S",
  69. 45: "4W",
  70. 50: "5",
  71. 70: "7",
  72. 80: "8",
  73. 85: "8X",
  74. }
  75. #: This dictionary can be used to produce a text version of the internal codes
  76. #: that Excel uses for error cells.
  77. error_text_from_code = {
  78. 0x00: '#NULL!', # Intersection of two cell ranges is empty
  79. 0x07: '#DIV/0!', # Division by zero
  80. 0x0F: '#VALUE!', # Wrong type of operand
  81. 0x17: '#REF!', # Illegal or deleted cell reference
  82. 0x1D: '#NAME?', # Wrong function or range name
  83. 0x24: '#NUM!', # Value range overflow
  84. 0x2A: '#N/A', # Argument or function not available
  85. }
  86. BIFF_FIRST_UNICODE = 80
  87. XL_WORKBOOK_GLOBALS = WBKBLOBAL = 0x5
  88. XL_WORKBOOK_GLOBALS_4W = 0x100
  89. XL_WORKSHEET = WRKSHEET = 0x10
  90. XL_BOUNDSHEET_WORKSHEET = 0x00
  91. XL_BOUNDSHEET_CHART = 0x02
  92. XL_BOUNDSHEET_VB_MODULE = 0x06
  93. # XL_RK2 = 0x7e
  94. XL_ARRAY = 0x0221
  95. XL_ARRAY2 = 0x0021
  96. XL_BLANK = 0x0201
  97. XL_BLANK_B2 = 0x01
  98. XL_BOF = 0x809
  99. XL_BOOLERR = 0x205
  100. XL_BOOLERR_B2 = 0x5
  101. XL_BOUNDSHEET = 0x85
  102. XL_BUILTINFMTCOUNT = 0x56
  103. XL_CF = 0x01B1
  104. XL_CODEPAGE = 0x42
  105. XL_COLINFO = 0x7D
  106. XL_COLUMNDEFAULT = 0x20 # BIFF2 only
  107. XL_COLWIDTH = 0x24 # BIFF2 only
  108. XL_CONDFMT = 0x01B0
  109. XL_CONTINUE = 0x3c
  110. XL_COUNTRY = 0x8C
  111. XL_DATEMODE = 0x22
  112. XL_DEFAULTROWHEIGHT = 0x0225
  113. XL_DEFCOLWIDTH = 0x55
  114. XL_DIMENSION = 0x200
  115. XL_DIMENSION2 = 0x0
  116. XL_EFONT = 0x45
  117. XL_EOF = 0x0a
  118. XL_EXTERNNAME = 0x23
  119. XL_EXTERNSHEET = 0x17
  120. XL_EXTSST = 0xff
  121. XL_FEAT11 = 0x872
  122. XL_FILEPASS = 0x2f
  123. XL_FONT = 0x31
  124. XL_FONT_B3B4 = 0x231
  125. XL_FORMAT = 0x41e
  126. XL_FORMAT2 = 0x1E # BIFF2, BIFF3
  127. XL_FORMULA = 0x6
  128. XL_FORMULA3 = 0x206
  129. XL_FORMULA4 = 0x406
  130. XL_GCW = 0xab
  131. XL_HLINK = 0x01B8
  132. XL_QUICKTIP = 0x0800
  133. XL_HORIZONTALPAGEBREAKS = 0x1b
  134. XL_INDEX = 0x20b
  135. XL_INTEGER = 0x2 # BIFF2 only
  136. XL_IXFE = 0x44 # BIFF2 only
  137. XL_LABEL = 0x204
  138. XL_LABEL_B2 = 0x04
  139. XL_LABELRANGES = 0x15f
  140. XL_LABELSST = 0xfd
  141. XL_LEFTMARGIN = 0x26
  142. XL_TOPMARGIN = 0x28
  143. XL_RIGHTMARGIN = 0x27
  144. XL_BOTTOMMARGIN = 0x29
  145. XL_HEADER = 0x14
  146. XL_FOOTER = 0x15
  147. XL_HCENTER = 0x83
  148. XL_VCENTER = 0x84
  149. XL_MERGEDCELLS = 0xE5
  150. XL_MSO_DRAWING = 0x00EC
  151. XL_MSO_DRAWING_GROUP = 0x00EB
  152. XL_MSO_DRAWING_SELECTION = 0x00ED
  153. XL_MULRK = 0xbd
  154. XL_MULBLANK = 0xbe
  155. XL_NAME = 0x18
  156. XL_NOTE = 0x1c
  157. XL_NUMBER = 0x203
  158. XL_NUMBER_B2 = 0x3
  159. XL_OBJ = 0x5D
  160. XL_PAGESETUP = 0xA1
  161. XL_PALETTE = 0x92
  162. XL_PANE = 0x41
  163. XL_PRINTGRIDLINES = 0x2B
  164. XL_PRINTHEADERS = 0x2A
  165. XL_RK = 0x27e
  166. XL_ROW = 0x208
  167. XL_ROW_B2 = 0x08
  168. XL_RSTRING = 0xd6
  169. XL_SCL = 0x00A0
  170. XL_SHEETHDR = 0x8F # BIFF4W only
  171. XL_SHEETPR = 0x81
  172. XL_SHEETSOFFSET = 0x8E # BIFF4W only
  173. XL_SHRFMLA = 0x04bc
  174. XL_SST = 0xfc
  175. XL_STANDARDWIDTH = 0x99
  176. XL_STRING = 0x207
  177. XL_STRING_B2 = 0x7
  178. XL_STYLE = 0x293
  179. XL_SUPBOOK = 0x1AE # aka EXTERNALBOOK in OOo docs
  180. XL_TABLEOP = 0x236
  181. XL_TABLEOP2 = 0x37
  182. XL_TABLEOP_B2 = 0x36
  183. XL_TXO = 0x1b6
  184. XL_UNCALCED = 0x5e
  185. XL_UNKNOWN = 0xffff
  186. XL_VERTICALPAGEBREAKS = 0x1a
  187. XL_WINDOW2 = 0x023E
  188. XL_WINDOW2_B2 = 0x003E
  189. XL_WRITEACCESS = 0x5C
  190. XL_WSBOOL = XL_SHEETPR
  191. XL_XF = 0xe0
  192. XL_XF2 = 0x0043 # BIFF2 version of XF record
  193. XL_XF3 = 0x0243 # BIFF3 version of XF record
  194. XL_XF4 = 0x0443 # BIFF4 version of XF record
  195. boflen = {0x0809: 8, 0x0409: 6, 0x0209: 6, 0x0009: 4}
  196. bofcodes = (0x0809, 0x0409, 0x0209, 0x0009)
  197. XL_FORMULA_OPCODES = (0x0006, 0x0406, 0x0206)
  198. _cell_opcode_list = [
  199. XL_BOOLERR,
  200. XL_FORMULA,
  201. XL_FORMULA3,
  202. XL_FORMULA4,
  203. XL_LABEL,
  204. XL_LABELSST,
  205. XL_MULRK,
  206. XL_NUMBER,
  207. XL_RK,
  208. XL_RSTRING,
  209. ]
  210. _cell_opcode_dict = {}
  211. for _cell_opcode in _cell_opcode_list:
  212. _cell_opcode_dict[_cell_opcode] = 1
  213. def is_cell_opcode(c):
  214. return c in _cell_opcode_dict
  215. def upkbits(tgt_obj, src, manifest, local_setattr=setattr):
  216. for n, mask, attr in manifest:
  217. local_setattr(tgt_obj, attr, (src & mask) >> n)
  218. def upkbitsL(tgt_obj, src, manifest, local_setattr=setattr, local_int=int):
  219. for n, mask, attr in manifest:
  220. local_setattr(tgt_obj, attr, local_int((src & mask) >> n))
  221. def unpack_string(data, pos, encoding, lenlen=1):
  222. nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0]
  223. pos += lenlen
  224. return unicode(data[pos:pos+nchars], encoding)
  225. def unpack_string_update_pos(data, pos, encoding, lenlen=1, known_len=None):
  226. if known_len is not None:
  227. # On a NAME record, the length byte is detached from the front of the string.
  228. nchars = known_len
  229. else:
  230. nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0]
  231. pos += lenlen
  232. newpos = pos + nchars
  233. return (unicode(data[pos:newpos], encoding), newpos)
  234. def unpack_unicode(data, pos, lenlen=2):
  235. "Return unicode_strg"
  236. nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0]
  237. if not nchars:
  238. # Ambiguous whether 0-length string should have an "options" byte.
  239. # Avoid crash if missing.
  240. return UNICODE_LITERAL("")
  241. pos += lenlen
  242. options = BYTES_ORD(data[pos])
  243. pos += 1
  244. # phonetic = options & 0x04
  245. # richtext = options & 0x08
  246. if options & 0x08:
  247. # rt = unpack('<H', data[pos:pos+2])[0] # unused
  248. pos += 2
  249. if options & 0x04:
  250. # sz = unpack('<i', data[pos:pos+4])[0] # unused
  251. pos += 4
  252. if options & 0x01:
  253. # Uncompressed UTF-16-LE
  254. rawstrg = data[pos:pos+2*nchars]
  255. # if DEBUG: print "nchars=%d pos=%d rawstrg=%r" % (nchars, pos, rawstrg)
  256. strg = unicode(rawstrg, 'utf_16_le')
  257. # pos += 2*nchars
  258. else:
  259. # Note: this is COMPRESSED (not ASCII!) encoding!!!
  260. # Merely returning the raw bytes would work OK 99.99% of the time
  261. # if the local codepage was cp1252 -- however this would rapidly go pear-shaped
  262. # for other codepages so we grit our Anglocentric teeth and return Unicode :-)
  263. strg = unicode(data[pos:pos+nchars], "latin_1")
  264. # pos += nchars
  265. # if richtext:
  266. # pos += 4 * rt
  267. # if phonetic:
  268. # pos += sz
  269. # return (strg, pos)
  270. return strg
  271. def unpack_unicode_update_pos(data, pos, lenlen=2, known_len=None):
  272. "Return (unicode_strg, updated value of pos)"
  273. if known_len is not None:
  274. # On a NAME record, the length byte is detached from the front of the string.
  275. nchars = known_len
  276. else:
  277. nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0]
  278. pos += lenlen
  279. if not nchars and not data[pos:]:
  280. # Zero-length string with no options byte
  281. return (UNICODE_LITERAL(""), pos)
  282. options = BYTES_ORD(data[pos])
  283. pos += 1
  284. phonetic = options & 0x04
  285. richtext = options & 0x08
  286. if richtext:
  287. rt = unpack('<H', data[pos:pos+2])[0]
  288. pos += 2
  289. if phonetic:
  290. sz = unpack('<i', data[pos:pos+4])[0]
  291. pos += 4
  292. if options & 0x01:
  293. # Uncompressed UTF-16-LE
  294. strg = unicode(data[pos:pos+2*nchars], 'utf_16_le')
  295. pos += 2*nchars
  296. else:
  297. # Note: this is COMPRESSED (not ASCII!) encoding!!!
  298. strg = unicode(data[pos:pos+nchars], "latin_1")
  299. pos += nchars
  300. if richtext:
  301. pos += 4 * rt
  302. if phonetic:
  303. pos += sz
  304. return (strg, pos)
  305. def unpack_cell_range_address_list_update_pos(output_list, data, pos, biff_version, addr_size=6):
  306. # output_list is updated in situ
  307. assert addr_size in (6, 8)
  308. # Used to assert size == 6 if not BIFF8, but pyWLWriter writes
  309. # BIFF8-only MERGEDCELLS records in a BIFF5 file!
  310. n, = unpack("<H", data[pos:pos+2])
  311. pos += 2
  312. if n:
  313. if addr_size == 6:
  314. fmt = "<HHBB"
  315. else:
  316. fmt = "<HHHH"
  317. for _unused in xrange(n):
  318. ra, rb, ca, cb = unpack(fmt, data[pos:pos+addr_size])
  319. output_list.append((ra, rb+1, ca, cb+1))
  320. pos += addr_size
  321. return pos
  322. _brecstrg = """\
  323. 0000 DIMENSIONS_B2
  324. 0001 BLANK_B2
  325. 0002 INTEGER_B2_ONLY
  326. 0003 NUMBER_B2
  327. 0004 LABEL_B2
  328. 0005 BOOLERR_B2
  329. 0006 FORMULA
  330. 0007 STRING_B2
  331. 0008 ROW_B2
  332. 0009 BOF_B2
  333. 000A EOF
  334. 000B INDEX_B2_ONLY
  335. 000C CALCCOUNT
  336. 000D CALCMODE
  337. 000E PRECISION
  338. 000F REFMODE
  339. 0010 DELTA
  340. 0011 ITERATION
  341. 0012 PROTECT
  342. 0013 PASSWORD
  343. 0014 HEADER
  344. 0015 FOOTER
  345. 0016 EXTERNCOUNT
  346. 0017 EXTERNSHEET
  347. 0018 NAME_B2,5+
  348. 0019 WINDOWPROTECT
  349. 001A VERTICALPAGEBREAKS
  350. 001B HORIZONTALPAGEBREAKS
  351. 001C NOTE
  352. 001D SELECTION
  353. 001E FORMAT_B2-3
  354. 001F BUILTINFMTCOUNT_B2
  355. 0020 COLUMNDEFAULT_B2_ONLY
  356. 0021 ARRAY_B2_ONLY
  357. 0022 DATEMODE
  358. 0023 EXTERNNAME
  359. 0024 COLWIDTH_B2_ONLY
  360. 0025 DEFAULTROWHEIGHT_B2_ONLY
  361. 0026 LEFTMARGIN
  362. 0027 RIGHTMARGIN
  363. 0028 TOPMARGIN
  364. 0029 BOTTOMMARGIN
  365. 002A PRINTHEADERS
  366. 002B PRINTGRIDLINES
  367. 002F FILEPASS
  368. 0031 FONT
  369. 0032 FONT2_B2_ONLY
  370. 0036 TABLEOP_B2
  371. 0037 TABLEOP2_B2
  372. 003C CONTINUE
  373. 003D WINDOW1
  374. 003E WINDOW2_B2
  375. 0040 BACKUP
  376. 0041 PANE
  377. 0042 CODEPAGE
  378. 0043 XF_B2
  379. 0044 IXFE_B2_ONLY
  380. 0045 EFONT_B2_ONLY
  381. 004D PLS
  382. 0051 DCONREF
  383. 0055 DEFCOLWIDTH
  384. 0056 BUILTINFMTCOUNT_B3-4
  385. 0059 XCT
  386. 005A CRN
  387. 005B FILESHARING
  388. 005C WRITEACCESS
  389. 005D OBJECT
  390. 005E UNCALCED
  391. 005F SAVERECALC
  392. 0063 OBJECTPROTECT
  393. 007D COLINFO
  394. 007E RK2_mythical_?
  395. 0080 GUTS
  396. 0081 WSBOOL
  397. 0082 GRIDSET
  398. 0083 HCENTER
  399. 0084 VCENTER
  400. 0085 BOUNDSHEET
  401. 0086 WRITEPROT
  402. 008C COUNTRY
  403. 008D HIDEOBJ
  404. 008E SHEETSOFFSET
  405. 008F SHEETHDR
  406. 0090 SORT
  407. 0092 PALETTE
  408. 0099 STANDARDWIDTH
  409. 009B FILTERMODE
  410. 009C FNGROUPCOUNT
  411. 009D AUTOFILTERINFO
  412. 009E AUTOFILTER
  413. 00A0 SCL
  414. 00A1 SETUP
  415. 00AB GCW
  416. 00BD MULRK
  417. 00BE MULBLANK
  418. 00C1 MMS
  419. 00D6 RSTRING
  420. 00D7 DBCELL
  421. 00DA BOOKBOOL
  422. 00DD SCENPROTECT
  423. 00E0 XF
  424. 00E1 INTERFACEHDR
  425. 00E2 INTERFACEEND
  426. 00E5 MERGEDCELLS
  427. 00E9 BITMAP
  428. 00EB MSO_DRAWING_GROUP
  429. 00EC MSO_DRAWING
  430. 00ED MSO_DRAWING_SELECTION
  431. 00EF PHONETIC
  432. 00FC SST
  433. 00FD LABELSST
  434. 00FF EXTSST
  435. 013D TABID
  436. 015F LABELRANGES
  437. 0160 USESELFS
  438. 0161 DSF
  439. 01AE SUPBOOK
  440. 01AF PROTECTIONREV4
  441. 01B0 CONDFMT
  442. 01B1 CF
  443. 01B2 DVAL
  444. 01B6 TXO
  445. 01B7 REFRESHALL
  446. 01B8 HLINK
  447. 01BC PASSWORDREV4
  448. 01BE DV
  449. 01C0 XL9FILE
  450. 01C1 RECALCID
  451. 0200 DIMENSIONS
  452. 0201 BLANK
  453. 0203 NUMBER
  454. 0204 LABEL
  455. 0205 BOOLERR
  456. 0206 FORMULA_B3
  457. 0207 STRING
  458. 0208 ROW
  459. 0209 BOF
  460. 020B INDEX_B3+
  461. 0218 NAME
  462. 0221 ARRAY
  463. 0223 EXTERNNAME_B3-4
  464. 0225 DEFAULTROWHEIGHT
  465. 0231 FONT_B3B4
  466. 0236 TABLEOP
  467. 023E WINDOW2
  468. 0243 XF_B3
  469. 027E RK
  470. 0293 STYLE
  471. 0406 FORMULA_B4
  472. 0409 BOF
  473. 041E FORMAT
  474. 0443 XF_B4
  475. 04BC SHRFMLA
  476. 0800 QUICKTIP
  477. 0809 BOF
  478. 0862 SHEETLAYOUT
  479. 0867 SHEETPROTECTION
  480. 0868 RANGEPROTECTION
  481. """
  482. biff_rec_name_dict = {}
  483. for _buff in _brecstrg.splitlines():
  484. _numh, _name = _buff.split()
  485. biff_rec_name_dict[int(_numh, 16)] = _name
  486. del _buff, _name, _brecstrg
  487. def hex_char_dump(strg, ofs, dlen, base=0, fout=sys.stdout, unnumbered=False):
  488. endpos = min(ofs + dlen, len(strg))
  489. pos = ofs
  490. numbered = not unnumbered
  491. num_prefix = ''
  492. while pos < endpos:
  493. endsub = min(pos + 16, endpos)
  494. substrg = strg[pos:endsub]
  495. lensub = endsub - pos
  496. if lensub <= 0 or lensub != len(substrg):
  497. fprintf(
  498. sys.stdout,
  499. '??? hex_char_dump: ofs=%d dlen=%d base=%d -> endpos=%d pos=%d endsub=%d substrg=%r\n',
  500. ofs, dlen, base, endpos, pos, endsub, substrg)
  501. break
  502. hexd = ''.join("%02x " % BYTES_ORD(c) for c in substrg)
  503. chard = ''
  504. for c in substrg:
  505. c = chr(BYTES_ORD(c))
  506. if c == '\0':
  507. c = '~'
  508. elif not (' ' <= c <= '~'):
  509. c = '?'
  510. chard += c
  511. if numbered:
  512. num_prefix = "%5d: " % (base+pos-ofs)
  513. fprintf(fout, "%s %-48s %s\n", num_prefix, hexd, chard)
  514. pos = endsub
  515. def biff_dump(mem, stream_offset, stream_len, base=0, fout=sys.stdout, unnumbered=False):
  516. pos = stream_offset
  517. stream_end = stream_offset + stream_len
  518. adj = base - stream_offset
  519. dummies = 0
  520. numbered = not unnumbered
  521. num_prefix = ''
  522. while stream_end - pos >= 4:
  523. rc, length = unpack('<HH', mem[pos:pos+4])
  524. if rc == 0 and length == 0:
  525. if mem[pos:] == b'\0' * (stream_end - pos):
  526. dummies = stream_end - pos
  527. savpos = pos
  528. pos = stream_end
  529. break
  530. if dummies:
  531. dummies += 4
  532. else:
  533. savpos = pos
  534. dummies = 4
  535. pos += 4
  536. else:
  537. if dummies:
  538. if numbered:
  539. num_prefix = "%5d: " % (adj + savpos)
  540. fprintf(fout, "%s---- %d zero bytes skipped ----\n", num_prefix, dummies)
  541. dummies = 0
  542. recname = biff_rec_name_dict.get(rc, '<UNKNOWN>')
  543. if numbered:
  544. num_prefix = "%5d: " % (adj + pos)
  545. fprintf(fout, "%s%04x %s len = %04x (%d)\n", num_prefix, rc, recname, length, length)
  546. pos += 4
  547. hex_char_dump(mem, pos, length, adj+pos, fout, unnumbered)
  548. pos += length
  549. if dummies:
  550. if numbered:
  551. num_prefix = "%5d: " % (adj + savpos)
  552. fprintf(fout, "%s---- %d zero bytes skipped ----\n", num_prefix, dummies)
  553. if pos < stream_end:
  554. if numbered:
  555. num_prefix = "%5d: " % (adj + pos)
  556. fprintf(fout, "%s---- Misc bytes at end ----\n", num_prefix)
  557. hex_char_dump(mem, pos, stream_end-pos, adj + pos, fout, unnumbered)
  558. elif pos > stream_end:
  559. fprintf(fout, "Last dumped record has length (%d) that is too large\n", length)
  560. def biff_count_records(mem, stream_offset, stream_len, fout=sys.stdout):
  561. pos = stream_offset
  562. stream_end = stream_offset + stream_len
  563. tally = {}
  564. while stream_end - pos >= 4:
  565. rc, length = unpack('<HH', mem[pos:pos+4])
  566. if rc == 0 and length == 0:
  567. if mem[pos:] == b'\0' * (stream_end - pos):
  568. break
  569. recname = "<Dummy (zero)>"
  570. else:
  571. recname = biff_rec_name_dict.get(rc, None)
  572. if recname is None:
  573. recname = "Unknown_0x%04X" % rc
  574. if recname in tally:
  575. tally[recname] += 1
  576. else:
  577. tally[recname] = 1
  578. pos += length + 4
  579. slist = sorted(tally.items())
  580. for recname, count in slist:
  581. print("%8d %s" % (count, recname), file=fout)
  582. encoding_from_codepage = {
  583. 1200 : 'utf_16_le',
  584. 10000: 'mac_roman',
  585. 10006: 'mac_greek', # guess
  586. 10007: 'mac_cyrillic', # guess
  587. 10029: 'mac_latin2', # guess
  588. 10079: 'mac_iceland', # guess
  589. 10081: 'mac_turkish', # guess
  590. 32768: 'mac_roman',
  591. 32769: 'cp1252',
  592. }
  593. # some more guessing, for Indic scripts
  594. # codepage 57000 range:
  595. # 2 Devanagari [0]
  596. # 3 Bengali [1]
  597. # 4 Tamil [5]
  598. # 5 Telegu [6]
  599. # 6 Assamese [1] c.f. Bengali
  600. # 7 Oriya [4]
  601. # 8 Kannada [7]
  602. # 9 Malayalam [8]
  603. # 10 Gujarati [3]
  604. # 11 Gurmukhi [2]