/src/CMake/Source/cmHexFileConverter.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmHexFileConverter.h" |
4 | | |
5 | | #include <cctype> |
6 | | #include <cstdio> |
7 | | #include <cstring> |
8 | | |
9 | | #include "cmSystemTools.h" |
10 | | |
11 | 0 | #define INTEL_HEX_MIN_LINE_LENGTH (1 + 8 + 2) |
12 | 0 | #define INTEL_HEX_MAX_LINE_LENGTH (1 + 8 + (256 * 2) + 2) |
13 | 0 | #define MOTOROLA_SREC_MIN_LINE_LENGTH (2 + 2 + 4 + 2) |
14 | 0 | #define MOTOROLA_SREC_MAX_LINE_LENGTH (2 + 2 + 8 + (256 * 2) + 2) |
15 | | |
16 | | static unsigned int ChompStrlen(char const* line) |
17 | 0 | { |
18 | 0 | if (!line) { |
19 | 0 | return 0; |
20 | 0 | } |
21 | 0 | unsigned int length = static_cast<unsigned int>(strlen(line)); |
22 | 0 | if ((line[length - 1] == '\n') || (line[length - 1] == '\r')) { |
23 | 0 | length--; |
24 | 0 | } |
25 | 0 | if ((line[length - 1] == '\n') || (line[length - 1] == '\r')) { |
26 | 0 | length--; |
27 | 0 | } |
28 | 0 | return length; |
29 | 0 | } |
30 | | |
31 | | static bool OutputBin(FILE* file, char const* buf, unsigned int startIndex, |
32 | | unsigned int stopIndex) |
33 | 0 | { |
34 | 0 | bool success = true; |
35 | 0 | char hexNumber[3]; |
36 | 0 | hexNumber[2] = '\0'; |
37 | 0 | char outBuf[256]; |
38 | 0 | unsigned int outBufCount = 0; |
39 | 0 | for (unsigned int i = startIndex; i < stopIndex; i += 2) { |
40 | 0 | hexNumber[0] = buf[i]; |
41 | 0 | hexNumber[1] = buf[i + 1]; |
42 | 0 | unsigned int convertedByte = 0; |
43 | 0 | if (sscanf(hexNumber, "%x", &convertedByte) != 1) { |
44 | 0 | success = false; |
45 | 0 | break; |
46 | 0 | } |
47 | 0 | outBuf[outBufCount] = static_cast<char>(convertedByte & 0xff); |
48 | 0 | outBufCount++; |
49 | 0 | } |
50 | 0 | if (success) { |
51 | 0 | success = (fwrite(outBuf, 1, outBufCount, file) == outBufCount); |
52 | 0 | } |
53 | 0 | return success; |
54 | 0 | } |
55 | | |
56 | | // see http://www.die.net/doc/linux/man/man5/srec.5.html |
57 | | static bool ConvertMotorolaSrecLine(char const* buf, FILE* outFile) |
58 | 0 | { |
59 | 0 | unsigned int slen = ChompStrlen(buf); |
60 | 0 | if ((slen < MOTOROLA_SREC_MIN_LINE_LENGTH) || |
61 | 0 | (slen > MOTOROLA_SREC_MAX_LINE_LENGTH)) { |
62 | 0 | return false; |
63 | 0 | } |
64 | | |
65 | | // line length must be even |
66 | 0 | if (slen % 2 == 1) { |
67 | 0 | return false; |
68 | 0 | } |
69 | | |
70 | 0 | if (buf[0] != 'S') { |
71 | 0 | return false; |
72 | 0 | } |
73 | | |
74 | 0 | unsigned int dataStart = 0; |
75 | | // ignore extra address records |
76 | 0 | if ((buf[1] == '5') || (buf[1] == '7') || (buf[1] == '8') || |
77 | 0 | (buf[1] == '9')) { |
78 | 0 | return true; |
79 | 0 | } |
80 | 0 | if (buf[1] == '1') { |
81 | 0 | dataStart = 8; |
82 | 0 | } else if (buf[1] == '2') { |
83 | 0 | dataStart = 10; |
84 | 0 | } else if (buf[1] == '3') { |
85 | 0 | dataStart = 12; |
86 | 0 | } else // unknown record type |
87 | 0 | { |
88 | 0 | return false; |
89 | 0 | } |
90 | | |
91 | | // ignore the last two bytes (checksum) |
92 | 0 | return OutputBin(outFile, buf, dataStart, slen - 2); |
93 | 0 | } |
94 | | |
95 | | // see http://en.wikipedia.org/wiki/Intel_hex |
96 | | static bool ConvertIntelHexLine(char const* buf, FILE* outFile) |
97 | 0 | { |
98 | 0 | unsigned int slen = ChompStrlen(buf); |
99 | 0 | if ((slen < INTEL_HEX_MIN_LINE_LENGTH) || |
100 | 0 | (slen > INTEL_HEX_MAX_LINE_LENGTH)) { |
101 | 0 | return false; |
102 | 0 | } |
103 | | |
104 | | // line length must be odd |
105 | 0 | if (slen % 2 == 0) { |
106 | 0 | return false; |
107 | 0 | } |
108 | | |
109 | 0 | if ((buf[0] != ':') || (buf[7] != '0')) { |
110 | 0 | return false; |
111 | 0 | } |
112 | | |
113 | 0 | unsigned int dataStart = 0; |
114 | 0 | if ((buf[8] == '0') || (buf[8] == '1')) { |
115 | 0 | dataStart = 9; |
116 | 0 | } |
117 | | // ignore extra address records |
118 | 0 | else if ((buf[8] == '2') || (buf[8] == '3') || (buf[8] == '4') || |
119 | 0 | (buf[8] == '5')) { |
120 | 0 | return true; |
121 | 0 | } else // unknown record type |
122 | 0 | { |
123 | 0 | return false; |
124 | 0 | } |
125 | | |
126 | | // ignore the last two bytes (checksum) |
127 | 0 | return OutputBin(outFile, buf, dataStart, slen - 2); |
128 | 0 | } |
129 | | |
130 | | cmHexFileConverter::FileType cmHexFileConverter::DetermineFileType( |
131 | | std::string const& inFileName) |
132 | 0 | { |
133 | 0 | char buf[1024]; |
134 | 0 | FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb"); |
135 | 0 | if (!inFile) { |
136 | 0 | return Binary; |
137 | 0 | } |
138 | | |
139 | 0 | if (!fgets(buf, 1024, inFile)) { |
140 | 0 | buf[0] = 0; |
141 | 0 | } |
142 | 0 | fclose(inFile); |
143 | 0 | FileType type = Binary; |
144 | 0 | unsigned int minLineLength = 0; |
145 | 0 | unsigned int maxLineLength = 0; |
146 | 0 | if (buf[0] == ':') // might be an intel hex file |
147 | 0 | { |
148 | 0 | type = IntelHex; |
149 | 0 | minLineLength = INTEL_HEX_MIN_LINE_LENGTH; |
150 | 0 | maxLineLength = INTEL_HEX_MAX_LINE_LENGTH; |
151 | 0 | } else if (buf[0] == 'S') // might be a motorola srec file |
152 | 0 | { |
153 | 0 | type = MotorolaSrec; |
154 | 0 | minLineLength = MOTOROLA_SREC_MIN_LINE_LENGTH; |
155 | 0 | maxLineLength = MOTOROLA_SREC_MAX_LINE_LENGTH; |
156 | 0 | } else { |
157 | 0 | return Binary; |
158 | 0 | } |
159 | | |
160 | 0 | unsigned int slen = ChompStrlen(buf); |
161 | 0 | if ((slen < minLineLength) || (slen > maxLineLength)) { |
162 | 0 | return Binary; |
163 | 0 | } |
164 | | |
165 | 0 | for (unsigned int i = 1; i < slen; i++) { |
166 | 0 | if (!isxdigit(buf[i])) { |
167 | 0 | return Binary; |
168 | 0 | } |
169 | 0 | } |
170 | 0 | return type; |
171 | 0 | } |
172 | | |
173 | | bool cmHexFileConverter::TryConvert(std::string const& inFileName, |
174 | | std::string const& outFileName) |
175 | 0 | { |
176 | 0 | FileType type = DetermineFileType(inFileName); |
177 | 0 | if (type == Binary) { |
178 | 0 | return false; |
179 | 0 | } |
180 | | |
181 | | // try to open the file |
182 | 0 | FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb"); |
183 | 0 | FILE* outFile = cmsys::SystemTools::Fopen(outFileName, "wb"); |
184 | 0 | if (!inFile || !outFile) { |
185 | 0 | if (inFile) { |
186 | 0 | fclose(inFile); |
187 | 0 | } |
188 | 0 | if (outFile) { |
189 | 0 | fclose(outFile); |
190 | 0 | } |
191 | 0 | return false; |
192 | 0 | } |
193 | | |
194 | | // convert them line by line |
195 | 0 | bool success = false; |
196 | 0 | char buf[1024]; |
197 | 0 | while (fgets(buf, 1024, inFile)) { |
198 | 0 | if (type == MotorolaSrec) { |
199 | 0 | success = ConvertMotorolaSrecLine(buf, outFile); |
200 | 0 | } else if (type == IntelHex) { |
201 | 0 | success = ConvertIntelHexLine(buf, outFile); |
202 | 0 | } |
203 | 0 | if (!success) { |
204 | 0 | break; |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | | // close them again |
209 | 0 | fclose(inFile); |
210 | 0 | fclose(outFile); |
211 | 0 | return success; |
212 | 0 | } |