Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 1 | #include "idmap.h" |
| 2 | |
| 3 | #include <androidfw/AssetManager.h> |
| 4 | #include <androidfw/ResourceTypes.h> |
Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 5 | #include <utils/ByteOrder.h> |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 6 | #include <utils/String8.h> |
| 7 | |
| 8 | #include <fcntl.h> |
| 9 | #include <sys/mman.h> |
| 10 | #include <sys/stat.h> |
| 11 | |
| 12 | using namespace android; |
| 13 | |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 14 | namespace { |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 15 | static const uint32_t IDMAP_MAGIC = 0x504D4449; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 16 | static const size_t PATH_LENGTH = 256; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 17 | |
| 18 | void printe(const char *fmt, ...); |
| 19 | |
| 20 | class IdmapBuffer { |
| 21 | private: |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 22 | const char* buf_; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 23 | size_t len_; |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 24 | size_t pos_; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 25 | public: |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 26 | IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {} |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 27 | |
| 28 | ~IdmapBuffer() { |
| 29 | if (buf_ != MAP_FAILED) { |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 30 | munmap(const_cast<char*>(buf_), len_); |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 31 | } |
| 32 | } |
| 33 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 34 | status_t init(const char *idmap_path) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 35 | struct stat st; |
| 36 | int fd; |
| 37 | |
| 38 | if (stat(idmap_path, &st) < 0) { |
| 39 | printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno)); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 40 | return UNKNOWN_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 41 | } |
| 42 | len_ = st.st_size; |
| 43 | if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) { |
| 44 | printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno)); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 45 | return UNKNOWN_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 46 | } |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 47 | if ((buf_ = (const char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 48 | close(fd); |
| 49 | printe("failed to mmap idmap: %s\n", strerror(errno)); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 50 | return UNKNOWN_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 51 | } |
| 52 | close(fd); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 53 | return NO_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 54 | } |
| 55 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 56 | status_t nextUint32(uint32_t* i) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 57 | if (!buf_) { |
| 58 | printe("failed to read next uint32_t: buffer not initialized\n"); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 59 | return UNKNOWN_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 60 | } |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 61 | |
| 62 | if (pos_ + sizeof(uint32_t) > len_) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 63 | printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n", |
| 64 | pos_); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 65 | return UNKNOWN_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 66 | } |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 67 | |
| 68 | if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x3) != 0) { |
| 69 | printe("failed to read next uint32_t: not aligned on 4-byte boundary\n"); |
| 70 | return UNKNOWN_ERROR; |
| 71 | } |
| 72 | |
| 73 | *i = dtohl(*reinterpret_cast<const uint32_t*>(buf_ + pos_)); |
| 74 | pos_ += sizeof(uint32_t); |
| 75 | return NO_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 76 | } |
| 77 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 78 | status_t nextUint16(uint16_t* i) { |
| 79 | if (!buf_) { |
| 80 | printe("failed to read next uint16_t: buffer not initialized\n"); |
| 81 | return UNKNOWN_ERROR; |
| 82 | } |
| 83 | |
| 84 | if (pos_ + sizeof(uint16_t) > len_) { |
| 85 | printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n", |
| 86 | pos_); |
| 87 | return UNKNOWN_ERROR; |
| 88 | } |
| 89 | |
| 90 | if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x1) != 0) { |
| 91 | printe("failed to read next uint32_t: not aligned on 2-byte boundary\n"); |
| 92 | return UNKNOWN_ERROR; |
| 93 | } |
| 94 | |
| 95 | *i = dtohs(*reinterpret_cast<const uint16_t*>(buf_ + pos_)); |
| 96 | pos_ += sizeof(uint16_t); |
| 97 | return NO_ERROR; |
| 98 | } |
| 99 | |
| 100 | status_t nextPath(char *b) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 101 | if (!buf_) { |
| 102 | printe("failed to read next path: buffer not initialized\n"); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 103 | return UNKNOWN_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 104 | } |
| 105 | if (pos_ + PATH_LENGTH > len_) { |
| 106 | printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 107 | return UNKNOWN_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 108 | } |
| 109 | memcpy(b, buf_ + pos_, PATH_LENGTH); |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 110 | pos_ += PATH_LENGTH; |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 111 | return NO_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 112 | } |
| 113 | }; |
| 114 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 115 | void printe(const char *fmt, ...) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 116 | va_list ap; |
| 117 | |
| 118 | va_start(ap, fmt); |
| 119 | fprintf(stderr, "error: "); |
| 120 | vfprintf(stderr, fmt, ap); |
| 121 | va_end(ap); |
| 122 | } |
| 123 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 124 | void print_header() { |
| 125 | printf("SECTION ENTRY VALUE COMMENT\n"); |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 126 | } |
| 127 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 128 | void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 129 | va_list ap; |
| 130 | |
| 131 | va_start(ap, fmt); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 132 | printf("%-12s %-12s 0x%08x ", section, subsection, value); |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 133 | vprintf(fmt, ap); |
| 134 | printf("\n"); |
| 135 | va_end(ap); |
| 136 | } |
| 137 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 138 | void print_path(const char *section, const char *subsection, const char *fmt, ...) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 139 | va_list ap; |
| 140 | |
| 141 | va_start(ap, fmt); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 142 | printf("%-12s %-12s .......... ", section, subsection); |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 143 | vprintf(fmt, ap); |
| 144 | printf("\n"); |
| 145 | va_end(ap); |
| 146 | } |
| 147 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 148 | status_t resource_metadata(const AssetManager& am, uint32_t res_id, |
| 149 | String8 *package, String8 *type, String8 *name) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 150 | const ResTable& rt = am.getResources(); |
| 151 | struct ResTable::resource_name data; |
| 152 | if (!rt.getResourceName(res_id, false, &data)) { |
| 153 | printe("failed to get resource name id=0x%08x\n", res_id); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 154 | return UNKNOWN_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 155 | } |
Andreas Gampe | cfedceb | 2014-09-30 21:48:18 -0700 | [diff] [blame] | 156 | if (package != NULL) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 157 | *package = String8(String16(data.package, data.packageLen)); |
| 158 | } |
Andreas Gampe | cfedceb | 2014-09-30 21:48:18 -0700 | [diff] [blame] | 159 | if (type != NULL) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 160 | *type = String8(String16(data.type, data.typeLen)); |
| 161 | } |
Andreas Gampe | cfedceb | 2014-09-30 21:48:18 -0700 | [diff] [blame] | 162 | if (name != NULL) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 163 | *name = String8(String16(data.name, data.nameLen)); |
| 164 | } |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 165 | return NO_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 166 | } |
| 167 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 168 | status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) { |
| 169 | uint32_t i; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 170 | char path[PATH_LENGTH]; |
| 171 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 172 | status_t err = buf.nextUint32(&i); |
| 173 | if (err != NO_ERROR) { |
| 174 | return err; |
| 175 | } |
| 176 | |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 177 | if (i != IDMAP_MAGIC) { |
| 178 | printe("not an idmap file: actual magic constant 0x%08x does not match expected magic " |
| 179 | "constant 0x%08x\n", i, IDMAP_MAGIC); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 180 | return UNKNOWN_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 181 | } |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 182 | |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 183 | print_header(); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 184 | print("IDMAP HEADER", "magic", i, ""); |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 185 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 186 | err = buf.nextUint32(&i); |
| 187 | if (err != NO_ERROR) { |
| 188 | return err; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 189 | } |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 190 | print("", "version", i, ""); |
| 191 | |
| 192 | err = buf.nextUint32(&i); |
| 193 | if (err != NO_ERROR) { |
| 194 | return err; |
| 195 | } |
| 196 | print("", "base crc", i, ""); |
| 197 | |
| 198 | err = buf.nextUint32(&i); |
| 199 | if (err != NO_ERROR) { |
| 200 | return err; |
| 201 | } |
| 202 | print("", "overlay crc", i, ""); |
| 203 | |
| 204 | err = buf.nextPath(path); |
| 205 | if (err != NO_ERROR) { |
| 206 | // printe done from IdmapBuffer::nextPath |
| 207 | return err; |
| 208 | } |
| 209 | print_path("", "base path", "%s", path); |
| 210 | |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 211 | if (!am.addAssetPath(String8(path), NULL)) { |
| 212 | printe("failed to add '%s' as asset path\n", path); |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 213 | return UNKNOWN_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 214 | } |
| 215 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 216 | err = buf.nextPath(path); |
| 217 | if (err != NO_ERROR) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 218 | // printe done from IdmapBuffer::nextPath |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 219 | return err; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 220 | } |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 221 | print_path("", "overlay path", "%s", path); |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 222 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 223 | return NO_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 224 | } |
| 225 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 226 | status_t parse_data(IdmapBuffer& buf, const AssetManager& am) { |
| 227 | const uint32_t packageId = am.getResources().getBasePackageId(0); |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 228 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 229 | uint16_t data16; |
| 230 | status_t err = buf.nextUint16(&data16); |
| 231 | if (err != NO_ERROR) { |
| 232 | return err; |
| 233 | } |
| 234 | print("DATA HEADER", "target pkg", static_cast<uint32_t>(data16), ""); |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 235 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 236 | err = buf.nextUint16(&data16); |
| 237 | if (err != NO_ERROR) { |
| 238 | return err; |
| 239 | } |
| 240 | print("", "types count", static_cast<uint32_t>(data16), ""); |
| 241 | |
| 242 | uint32_t typeCount = static_cast<uint32_t>(data16); |
| 243 | while (typeCount > 0) { |
| 244 | typeCount--; |
| 245 | |
| 246 | err = buf.nextUint16(&data16); |
| 247 | if (err != NO_ERROR) { |
| 248 | return err; |
| 249 | } |
| 250 | const uint32_t targetTypeId = static_cast<uint32_t>(data16); |
| 251 | print("DATA BLOCK", "target type", targetTypeId, ""); |
| 252 | |
| 253 | err = buf.nextUint16(&data16); |
| 254 | if (err != NO_ERROR) { |
| 255 | return err; |
| 256 | } |
| 257 | print("", "overlay type", static_cast<uint32_t>(data16), ""); |
| 258 | |
| 259 | err = buf.nextUint16(&data16); |
| 260 | if (err != NO_ERROR) { |
| 261 | return err; |
| 262 | } |
| 263 | const uint32_t entryCount = static_cast<uint32_t>(data16); |
| 264 | print("", "entry count", entryCount, ""); |
| 265 | |
| 266 | err = buf.nextUint16(&data16); |
| 267 | if (err != NO_ERROR) { |
| 268 | return err; |
| 269 | } |
| 270 | const uint32_t entryOffset = static_cast<uint32_t>(data16); |
| 271 | print("", "entry offset", entryOffset, ""); |
| 272 | |
| 273 | for (uint32_t i = 0; i < entryCount; i++) { |
| 274 | uint32_t data32; |
| 275 | err = buf.nextUint32(&data32); |
| 276 | if (err != NO_ERROR) { |
| 277 | return err; |
| 278 | } |
| 279 | |
| 280 | uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i); |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 281 | String8 type; |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 282 | String8 name; |
| 283 | err = resource_metadata(am, resID, NULL, &type, &name); |
| 284 | if (err != NO_ERROR) { |
| 285 | return err; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 286 | } |
MÃ¥rten Kongstad | ea2a1b9 | 2016-02-29 14:12:35 +0100 | [diff] [blame] | 287 | if (data32 != ResTable_type::NO_ENTRY) { |
| 288 | print("", "entry", data32, "%s/%s", type.string(), name.string()); |
| 289 | } |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 290 | } |
| 291 | } |
| 292 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 293 | return NO_ERROR; |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 294 | } |
| 295 | } |
| 296 | |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 297 | int idmap_inspect(const char *idmap_path) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 298 | IdmapBuffer buf; |
| 299 | if (buf.init(idmap_path) < 0) { |
| 300 | // printe done from IdmapBuffer::init |
| 301 | return EXIT_FAILURE; |
| 302 | } |
| 303 | AssetManager am; |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 304 | if (parse_idmap_header(buf, am) != NO_ERROR) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 305 | // printe done from parse_idmap_header |
| 306 | return EXIT_FAILURE; |
| 307 | } |
Adam Lesinski | f90f2f8d | 2014-06-06 14:27:00 -0700 | [diff] [blame] | 308 | if (parse_data(buf, am) != NO_ERROR) { |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 309 | // printe done from parse_data_header |
| 310 | return EXIT_FAILURE; |
| 311 | } |
Colin Cross | 13221c9 | 2014-02-11 18:04:44 -0800 | [diff] [blame] | 312 | return EXIT_SUCCESS; |
| 313 | } |