I. Linux xxd-i features
The Linux system xxd command displays the file contents in binary or hexadecimal format. If the outfile parameter is not specified, the result is displayed on the terminal screen, otherwise it is output to outfile. For detailed usage, refer to Linux command xxd.
This article focuses on the XXD command-i option. Use this option to output a C-language array definition that is named Inputfile. For example, after performing the echo 12345 > Test and xxd-i Test commands, the output is:
unsigned char test[] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x0a};unsigned int test_len = 6;
Visible, the array name is the input file name (if there is a suffix, the dot is replaced with an underscore). Note that 0x0a represents the newline character lf, or ' \ n '.
Two. Xxd-i Common uses
When a device does not have a file system or does not support dynamic memory management, it is sometimes stored in a static array of C code for binary files, such as bootstrapper and firmware. At this point, the version array can be automatically generated with the XXD command. Examples are as follows:
1) Use the Linux command xdd to convert binary vdslbooter.bin to 16 binary DslBooter.txt:
Xxd-i < Vdslbooter.bin > DslBooter.txt
Where the '-I ' option indicates the style of the output as C contains the file (array mode). Redirect symbol ' < ' redirects the contents of the Vdslbooter.bin file to standard input, which rejects the array declaration and length variable definitions so that the output contains only 16 binary values.
2) define the appropriate static array within the C code source file:
static const Uint8 bootimagearray[] = {#include ": /.. /dslbooter.txt "}; Targetimage bootimage = {(Uint8 *) bootimagearray,sizeof (Bootimagearray)/sizeof (bootimagearray[0])};
When compiling the source code, the contents of the DslBooter.txt file are automatically expanded into the above array. By skillfully using # include preprocessing directives, you can eliminate the hassle of copying array contents manually.
Three. Python implementation of class Xxd-i function
This section uses the Python2.7 language to implement features similar to Xxd-i.
Because the author is in the learning phase, there are many different ways of writing in the code, but the function is identical or similar, in order to provide different grammatical reference, please understand.
First, consider a short but complete program (saved as xddi.py):
#!/usr/bin/python#coding=utf-8# determines if the c keyword ckeywords = ("Auto", "Break", "Case", "char", "Const", "Continue", "default", ' Do ', ' double ', ' Else ', ' enum ', ' extern ', ' float ', ' for ', ' goto ', ' if ', ' int ', ' long ', ' register ', ' return ', ' short ', ' Signed "," Static "," sizeof "," struct "," switch "," typedef "," union "," unsigned "," void "," volatile "," while "," _bool ") #_ BOOL for C99 new keyword def isckeywords (name): For x in Ckeywords:if CMP (x, name) = = 0:return Truereturn Falseif __name__ = = ' __main__ ' :p rint isckeywords (' const ') #Xxdi ()
This code determines whether the given string is a C keyword. At the Windows system cmd command prompt, enter E:\pytest>python xxdi.py, and the execution result is true.
The next code snippet omits the script and the code declaration of the header, as well as the trailing ' main ' segment.
Before generating a C array, ensure that the array name is valid. A C language identifier can consist only of letters, numbers, and underscores, and cannot begin with a number. Additionally, the keyword cannot be used as an identifier. All, the illegal characters need to be processed, its rules see code Comments:
Import Redef Generatecarrayname (inFile): #字母数字下划线以外的字符均转为下划线 # ' int $=5; ' The definition of GCC 4.1.2 can be compiled through, but is still considered an illegal identifier inFile = Re.sub (' [^0-9a-za-z\_] ', ' _ ', InFile) # ' _ ' to ' remove the illegal character # number beginning with double underline if infile[0]. IsDigit () = = True:infile = ' __ ' + infile# if the input file name is a c keyword, capitalize it with an underscore suffix as the array name # cannot be simply capitalized or underlined before it is easy for user-defined names to conflict if Isckeywords ( InFile) is True:infile = '%s_ '%infile.upper () return inFile
When executed with print generatecarrayname (' 1a$if1#1_4.txt '), the incoming parameter string is converted to __1a_if1_1_4_txt. Similarly, _bool is converted to _bool_.
To simulate the Linux command style as much as possible, you also need to provide command-line options and parameters. The parsing module chooses Optionparser, and its usage is described in Python command-line parsing. The command line implementation of the class Xxd-i feature is as follows:
#def parseoption (base, cols, strip, InFile, outFile):d ef parseoption (base = +, cols = A, strip = False, InFile = ", out File = None): from optparse import optionparsercustusage = ' \ n xxdi (. py) [options] inFile [outFile] ' parser = optionparser (u Sage=custusage) parser.add_option ('-B ', '--base ', dest= ' base ', help= ' represent values according to base (default:16) ') Parser.add_option ('-C ', '--column ', dest= ' col ', help= ' col octets per line (default:12) ') parser.add_option ('-s ', '-- Strip ', action= ' store_true ', dest= ' strip ', help= ' only output C array elements ') (options, args) = Parser.parse_args () if Options.base is not none:base = Int (options.base) If options.col are not none:cols = Int (options.col) if Options.strip are not None:strip = Trueif len (args) = = 0:print ' No argument, at least one (inFile)!\nusage:%s '%custusageif len (args) >= 1:in File = Args[0]if len (args) >= 2:outfile = Args[1]return ([base, cols, strip], [InFile, OutFile])
The Def parseoption (...) being commented out. It was originally called in the following way:
base = 16; cols = 12; Strip = False; InFile = "; OutFile = "([Base, cols, strip], [InFile, outFile]) = Parseoption (Base,cols, Strip, InFile, OutFile)
The intent is to simultaneously modify the values of base, cols, strip and other parameters. However, this is a very awkward way to use the default parameters of the function definition, the call only need to write Parseoption (). If the reader knows a better wording, hope to enlighten.
Bring up the command prompt with the-h option, which is very close to the Linux style:
E:\pytest>python xxdi.py-husage:xxdi (. py) [options] inFile [Outfile]options:-h,--help Show this help message and exit -B BASE,--base=base represent values according to BASE (default:16)-c col,--column=col Col octets per line (default:12)-S, --strip only output C array elements
Based on the exercises above, then complete the play in this article:
Def xxdi (): #解析命令行选项及参数 ([Base, cols, strip], [InFile, outFile]) = parseoption () import osif os.path.isfile (inFile) is False :p rint "'%s ' is not a file! '"%infilereturnwith open (InFile, ' RB ') as file: #必须以 ' B ' mode access binary file #file = open (InFile, ' RB ') #Python2.5 The following version does not support with...as syntax # if True: #不用for line in file or ReadLine (s) in case the ' 0x0a ' wrap content = File.read () #将文件内容 "Break" is a byte array if base is a: #Hexadecimalcontent = map (Lambda X:hex (ord (x)), content) Elif base is: #Decimalcontent = map (lambda X:str (ord (x)), content) Elif Base is 8: #Octalcontent = map (Lambda x:oct (ord (x)), content) Else:print ' [%s]: Invalid base or radix for C language! '%basereturn# constructs an array that defines the header and length variables carrayname = generatecarrayname (inFile) if strip is False:carrayheader = ' unsigned char%s[] = {'%carraynameelse:carrayheader = ' Carraytailer = '};\nunsigned int%s_len =%d; '% (Carrayname, le N (content)) if strip is True:carraytailer = "#print会在每行输出后自动换行if outFile is none:print carrayheaderfor i in range (0, Len ( Content), cols): line = ', '. Join (ConTent[i:i+cols]) print ' + line + ', ' Print Carraytailerreturnwith open (outFile, ' W ') as file: #file = open (OutFile, ' W ') #P ython2.5 The following versions do not support with...as syntax # if True:file.write (Carrayheader + ' \ n ') for I in a range (0, Len (content), cols): line = reduce ( Lambda x, y: ', ' Join ([x, Y]), Content[i:i+cols]) file.write ('%s,\n '%line) File.flush () File.write (Carraytailer)
Python2.5 The following versions do not support with...as syntax, and the Linux system used by author debugging is only loaded with Python2.4.3. Therefore, to run xddi.py on a Linux system, write only as File = Open (...). However, this requires handling file closures and exceptions, as described in understanding Python's with...as ... Grammar. Note that you need to declare the from __future__ import with_statement when using with...as syntax in Python2.5.
The Python version number can be obtained by platform.python_version (). For example:
Import platform# Determine if Python is Major.minor and above version def isforwardpyversion (Major, minor): #python_version () return ' major. Minor.patchlevel ', such as ' 2.7.11 ' ver = platform.python_version (). Split ('. ') if Int (ver[0]) >= Major and Int (ver[1]) >= Minor:return Truereturn False
After a two-step test of Windows and Linux systems, the Xddi () work basically meets expectations. As an example of the 123456789ABCDEF.txt file (123456789ABCDEF), the test results are as follows:
E:\pytest>python xxdi.py-c 5-b 2-s 123456789abcdef.txt[2]: Invalid base or radix for C language! E:\pytest>python xxdi.py-c 5-b 10-s 123456789abcdef.txt49, Wu, Wu, 53,54,--------, 65,66 Ytest>python xxdi.py-c 5-b 123456789abcdef.txtunsigned char __123456789abcdef_txt[] = {49, 50, 51, 52, 53, 54, 55, 65,66, 70,};unsigned int __123456789abcdef_txt_len = 15, the. E:\pytest>python xxdi.py-c 5-b 8 123456789abcdef.txtunsigned char __123456789abcdef_txt[] = {061, 062, 063, 064, 065, 066, 067, 070, 071, 0101,0102, 0103, 0104, 0105, 0106,};unsigned int __123456789abcdef_txt_len = 15; E:\pytest>python xxdi.py 123456789abcdef.txtunsigned char __123456789abcdef_txt[] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x3 6, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43,0x44, 0x45, 0x46,};unsigned int __123456789abcdef_txt_len = 15;
In the case of a slightly larger two-level file, for example, after executing python xxdi.py vdslbooter.bin booter.c, the contents of the Booter.c file are as follows (intercept):
unsigned char vdslbooter_bin[] = {0xFF, 0x31, 0x0, 0xb, 0xFF, 0x3, 0x1f, 0x5a, 0x0, 0x0, 0x0, 0x0,//..... 0x0, 0x0, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,};unsigned int Vdslboo Ter_bin_len = 53588;
In conclusion, the author realizes the Xxdi module and the Linux xxd-i function very close, and each has the merits and demerits. The advantage of Xxdi is that the validity of the logarithm of the group name checks more fully (the keyword check), the array content representation richer (8 and 10); The disadvantage is that redirection is not supported and the numeric widths are not fixed (such as 0XB and 0xFF). Of course, these shortcomings are not difficult to eliminate. For example, the ' 0x%02x '%val is used instead of Hex (Val) to control the output bit width. It's just that adding perfection inevitably increases the complexity of the code and may be inefficient.
The above is a small series to introduce you to the Python implementation of the Linux command xxd-i function, I hope to help you above!