github.com/metux/go-metabuild@v0.0.0-20240118143255-d9ed5ab697f9/util/strs/xxd.go (about) 1 package strs 2 3 import ( 4 "bufio" 5 "io" 6 "os" 7 "path/filepath" 8 "strconv" 9 ) 10 11 const ( 12 ldigits = "0123456789abcdef" 13 udigits = "0123456789ABCDEF" 14 ) 15 16 var ( 17 space = []byte(" ") 18 doubleSpace = []byte(" ") 19 dot = []byte(".") 20 newLine = []byte("\n") 21 unsignedChar = []byte("unsigned char ") 22 unsignedInt = []byte("};\nunsigned int ") 23 lenEquals = []byte("_len = ") 24 brackets = []byte("[] = {") 25 asterisk = []byte("*") 26 commaSpace = []byte(", ") 27 comma = []byte(",") 28 semiColonNl = []byte(";\n") 29 bar = []byte("|") 30 ) 31 32 func cfmtEncode(dst, src []byte, hextable string) { 33 b := src[0] 34 dst[3] = hextable[b&0x0f] 35 dst[2] = hextable[b>>4] 36 dst[1] = 'x' 37 dst[0] = '0' 38 } 39 40 func xxd(r io.Reader, w io.Writer, fname string) error { 41 var ( 42 cols int 43 octs int 44 caps = ldigits 45 doCHeader = true 46 doCEnd bool 47 // enough room for "unsigned char NAME_FORMAT[] = {" 48 varDeclChar = make([]byte, 14+len(fname)+6) 49 // enough room for "unsigned int NAME_FORMAT = " 50 varDeclInt = make([]byte, 16+len(fname)+7) 51 ) 52 53 // Generate the first and last line in the -i output: 54 // e.g. unsigned char foo_txt[] = { and unsigned int foo_txt_len = 55 // copy over "unnsigned char " and "unsigned int" 56 _ = copy(varDeclChar[0:14], unsignedChar[:]) 57 _ = copy(varDeclInt[0:16], unsignedInt[:]) 58 59 for i := 0; i < len(fname); i++ { 60 if fname[i] != '.' { 61 varDeclChar[14+i] = fname[i] 62 varDeclInt[16+i] = fname[i] 63 } else { 64 varDeclChar[14+i] = '_' 65 varDeclInt[16+i] = '_' 66 } 67 } 68 // copy over "[] = {" and "_len = " 69 _ = copy(varDeclChar[14+len(fname):], brackets[:]) 70 _ = copy(varDeclInt[16+len(fname):], lenEquals[:]) 71 72 cols = 12 73 octs = 4 74 75 // These are bumped down from the beginning of the function in order to 76 // allow for their sizes to be allocated based on the user's speficiations 77 var ( 78 line = make([]byte, cols) 79 char = make([]byte, octs) 80 ) 81 82 c := int64(0) // number of characters 83 r = bufio.NewReader(r) 84 85 for { 86 n, err := io.ReadFull(r, line) 87 if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { 88 return err 89 } 90 91 if n == 0 { 92 doCEnd = true 93 } 94 95 if doCHeader { 96 w.Write(varDeclChar) 97 w.Write(newLine) 98 doCHeader = false 99 } 100 101 // C values 102 if !doCEnd { 103 w.Write(doubleSpace) 104 } 105 for i := 0; i < n; i++ { 106 cfmtEncode(char, line[i:i+1], caps) 107 w.Write(char) 108 c++ 109 // don't add spaces to EOL 110 if i != n-1 { 111 w.Write(commaSpace) 112 } else if n == cols { 113 w.Write(comma) 114 } 115 } 116 117 if doCEnd { 118 w.Write(varDeclInt) 119 w.Write([]byte(strconv.FormatInt(c, 10))) 120 w.Write(semiColonNl) 121 return nil 122 } 123 124 w.Write(newLine) 125 } 126 return nil 127 } 128 129 func XXD(in string, out string) error { 130 inFile, err := os.Open(in) 131 if err != nil { 132 return err 133 } 134 defer inFile.Close() 135 136 outFile, err := os.Create(out) 137 if err != nil { 138 return err 139 } 140 defer outFile.Close() 141 142 outwr := bufio.NewWriter(outFile) 143 defer outwr.Flush() 144 145 return xxd(inFile, outwr, filepath.Base(in)) 146 }