| ... | ... | @@ -15,6 +15,12 @@ name: []const u8, |
| 15 | 15 | |
| 16 | 16 | header: ar_hdr = undefined, |
| 17 | 17 | |
| 18 | /// A list of long file names, delimited by a LF character (0x0a). |
| 19 | /// This is stored as a single slice of bytes, as the header-names |
| 20 | /// point to the character index of a file name, rather than the index |
| 21 | /// in the list. |
| 22 | long_file_names: []const u8 = undefined, |
| 23 | |
| 18 | 24 | /// Parsed table of contents. |
| 19 | 25 | /// Each symbol name points to a list of all definition |
| 20 | 26 | /// sites within the current static archive. |
| ... | ... | @@ -53,32 +59,33 @@ const ar_hdr = extern struct { |
| 53 | 59 | /// Always contains ARFMAG. |
| 54 | 60 | ar_fmag: [2]u8, |
| 55 | 61 | |
| 56 | | const NameOrLength = union(enum) { |
| 57 | | Name: []const u8, |
| 58 | | Length: u32, |
| 62 | const NameOrIndex = union(enum) { |
| 63 | name: []const u8, |
| 64 | index: u32, |
| 59 | 65 | }; |
| 60 | | fn nameOrLength(self: ar_hdr) !NameOrLength { |
| 61 | | const value = getValue(&self.ar_name); |
| 66 | |
| 67 | fn nameOrIndex(archive: ar_hdr) !NameOrIndex { |
| 68 | const value = getValue(&archive.ar_name); |
| 62 | 69 | const slash_index = mem.indexOfScalar(u8, value, '/') orelse return error.MalformedArchive; |
| 63 | 70 | const len = value.len; |
| 64 | 71 | if (slash_index == len - 1) { |
| 65 | 72 | // Name stored directly |
| 66 | | return NameOrLength{ .Name = value }; |
| 73 | return NameOrIndex{ .name = value }; |
| 67 | 74 | } else { |
| 68 | 75 | // Name follows the header directly and its length is encoded in |
| 69 | 76 | // the name field. |
| 70 | | const length = try std.fmt.parseInt(u32, value[slash_index + 1 ..], 10); |
| 71 | | return NameOrLength{ .Length = length }; |
| 77 | const index = try std.fmt.parseInt(u32, value[slash_index + 1 ..], 10); |
| 78 | return NameOrIndex{ .index = index }; |
| 72 | 79 | } |
| 73 | 80 | } |
| 74 | 81 | |
| 75 | | fn date(self: ar_hdr) !u64 { |
| 76 | | const value = getValue(&self.ar_date); |
| 82 | fn date(archive: ar_hdr) !u64 { |
| 83 | const value = getValue(&archive.ar_date); |
| 77 | 84 | return std.fmt.parseInt(u64, value, 10); |
| 78 | 85 | } |
| 79 | 86 | |
| 80 | | fn size(self: ar_hdr) !u32 { |
| 81 | | const value = getValue(&self.ar_size); |
| 87 | fn size(archive: ar_hdr) !u32 { |
| 88 | const value = getValue(&archive.ar_size); |
| 82 | 89 | return std.fmt.parseInt(u32, value, 10); |
| 83 | 90 | } |
| 84 | 91 | |
| ... | ... | @@ -87,18 +94,19 @@ const ar_hdr = extern struct { |
| 87 | 94 | } |
| 88 | 95 | }; |
| 89 | 96 | |
| 90 | | pub fn deinit(self: *Archive, allocator: Allocator) void { |
| 91 | | for (self.toc.keys()) |*key| { |
| 97 | pub fn deinit(archive: *Archive, allocator: Allocator) void { |
| 98 | for (archive.toc.keys()) |*key| { |
| 92 | 99 | allocator.free(key.*); |
| 93 | 100 | } |
| 94 | | for (self.toc.values()) |*value| { |
| 101 | for (archive.toc.values()) |*value| { |
| 95 | 102 | value.deinit(allocator); |
| 96 | 103 | } |
| 97 | | self.toc.deinit(allocator); |
| 104 | archive.toc.deinit(allocator); |
| 105 | allocator.free(archive.long_file_names); |
| 98 | 106 | } |
| 99 | 107 | |
| 100 | | pub fn parse(self: *Archive, allocator: Allocator) !void { |
| 101 | | const reader = self.file.reader(); |
| 108 | pub fn parse(archive: *Archive, allocator: Allocator) !void { |
| 109 | const reader = archive.file.reader(); |
| 102 | 110 | |
| 103 | 111 | const magic = try reader.readBytesNoEof(SARMAG); |
| 104 | 112 | if (!mem.eql(u8, &magic, ARMAG)) { |
| ... | ... | @@ -106,38 +114,31 @@ pub fn parse(self: *Archive, allocator: Allocator) !void { |
| 106 | 114 | return error.NotArchive; |
| 107 | 115 | } |
| 108 | 116 | |
| 109 | | self.header = try reader.readStruct(ar_hdr); |
| 110 | | if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) { |
| 111 | | log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.ar_fmag }); |
| 117 | archive.header = try reader.readStruct(ar_hdr); |
| 118 | if (!mem.eql(u8, &archive.header.ar_fmag, ARFMAG)) { |
| 119 | log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, archive.header.ar_fmag }); |
| 112 | 120 | return error.NotArchive; |
| 113 | 121 | } |
| 114 | 122 | |
| 115 | | try self.parseTableOfContents(allocator, reader); |
| 123 | try archive.parseTableOfContents(allocator, reader); |
| 124 | try archive.parseNameTable(allocator, reader); |
| 116 | 125 | } |
| 117 | 126 | |
| 118 | | fn parseName(allocator: Allocator, header: ar_hdr, reader: anytype) ![]u8 { |
| 119 | | const name_or_length = try header.nameOrLength(); |
| 120 | | var name: []u8 = undefined; |
| 121 | | switch (name_or_length) { |
| 122 | | .Name => |n| { |
| 123 | | name = try allocator.dupe(u8, n); |
| 124 | | }, |
| 125 | | .Length => |len| { |
| 126 | | var n = try allocator.alloc(u8, len); |
| 127 | | defer allocator.free(n); |
| 128 | | try reader.readNoEof(n); |
| 129 | | const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)) orelse n.len; |
| 130 | | name = try allocator.dupe(u8, n[0..actual_len]); |
| 127 | fn parseName(archive: *const Archive, header: ar_hdr) ![]const u8 { |
| 128 | const name_or_index = try header.nameOrIndex(); |
| 129 | switch (name_or_index) { |
| 130 | .name => |name| return name, |
| 131 | .index => |index| { |
| 132 | const name = mem.sliceTo(archive.long_file_names[index..], 0x0a); |
| 133 | return mem.trimRight(u8, name, "/"); |
| 131 | 134 | }, |
| 132 | 135 | } |
| 133 | | return name; |
| 134 | 136 | } |
| 135 | 137 | |
| 136 | | fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !void { |
| 137 | | log.debug("parsing table of contents for archive file '{s}'", .{self.name}); |
| 138 | fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype) !void { |
| 138 | 139 | // size field can have extra spaces padded in front as well as the end, |
| 139 | 140 | // so we trim those first before parsing the ASCII value. |
| 140 | | const size_trimmed = std.mem.trim(u8, &self.header.ar_size, " "); |
| 141 | const size_trimmed = mem.trim(u8, &archive.header.ar_size, " "); |
| 141 | 142 | const sym_tab_size = try std.fmt.parseInt(u32, size_trimmed, 10); |
| 142 | 143 | |
| 143 | 144 | const num_symbols = try reader.readIntBig(u32); |
| ... | ... | @@ -157,7 +158,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) ! |
| 157 | 158 | |
| 158 | 159 | var i: usize = 0; |
| 159 | 160 | while (i < sym_tab.len) { |
| 160 | | const string = std.mem.sliceTo(sym_tab[i..], 0); |
| 161 | const string = mem.sliceTo(sym_tab[i..], 0); |
| 161 | 162 | if (string.len == 0) { |
| 162 | 163 | i += 1; |
| 163 | 164 | continue; |
| ... | ... | @@ -165,7 +166,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) ! |
| 165 | 166 | i += string.len; |
| 166 | 167 | const name = try allocator.dupe(u8, string); |
| 167 | 168 | errdefer allocator.free(name); |
| 168 | | const gop = try self.toc.getOrPut(allocator, name); |
| 169 | const gop = try archive.toc.getOrPut(allocator, name); |
| 169 | 170 | if (gop.found_existing) { |
| 170 | 171 | allocator.free(name); |
| 171 | 172 | } else { |
| ... | ... | @@ -175,33 +176,49 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) ! |
| 175 | 176 | } |
| 176 | 177 | } |
| 177 | 178 | |
| 179 | fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !void { |
| 180 | const header: ar_hdr = try reader.readStruct(ar_hdr); |
| 181 | if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) { |
| 182 | log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag }); |
| 183 | return error.MalformedArchive; |
| 184 | } |
| 185 | if (!mem.eql(u8, header.ar_name[0..2], "//")) { |
| 186 | log.err("invalid archive. Long name table missing", .{}); |
| 187 | return error.MalformedArchive; |
| 188 | } |
| 189 | const table_size = try header.size(); |
| 190 | const long_file_names = try allocator.alloc(u8, table_size); |
| 191 | errdefer allocator.free(long_file_names); |
| 192 | try reader.readNoEof(long_file_names); |
| 193 | archive.long_file_names = long_file_names; |
| 194 | } |
| 195 | |
| 178 | 196 | /// From a given file offset, starts reading for a file header. |
| 179 | 197 | /// When found, parses the object file into an `Object` and returns it. |
| 180 | | pub fn parseObject(self: Archive, allocator: Allocator, file_offset: u32) !Object { |
| 181 | | try self.file.seekTo(file_offset); |
| 182 | | const reader = self.file.reader(); |
| 198 | pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Object { |
| 199 | try archive.file.seekTo(file_offset); |
| 200 | const reader = archive.file.reader(); |
| 183 | 201 | const header = try reader.readStruct(ar_hdr); |
| 184 | | const current_offset = try self.file.getPos(); |
| 185 | | try self.file.seekTo(0); |
| 202 | const current_offset = try archive.file.getPos(); |
| 203 | try archive.file.seekTo(0); |
| 186 | 204 | |
| 187 | 205 | if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) { |
| 188 | 206 | log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag }); |
| 189 | 207 | return error.MalformedArchive; |
| 190 | 208 | } |
| 191 | 209 | |
| 192 | | const object_name = try parseName(allocator, header, reader); |
| 193 | | defer allocator.free(object_name); |
| 194 | | |
| 210 | const object_name = try archive.parseName(header); |
| 195 | 211 | const name = name: { |
| 196 | 212 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 197 | | const path = try std.os.realpath(self.name, &buffer); |
| 213 | const path = try std.os.realpath(archive.name, &buffer); |
| 198 | 214 | break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name }); |
| 199 | 215 | }; |
| 200 | 216 | defer allocator.free(name); |
| 201 | 217 | |
| 202 | | const object_file = try std.fs.cwd().openFile(self.name, .{}); |
| 218 | const object_file = try std.fs.cwd().openFile(archive.name, .{}); |
| 203 | 219 | errdefer object_file.close(); |
| 204 | 220 | |
| 221 | const object_file_size = try header.size(); |
| 205 | 222 | try object_file.seekTo(current_offset); |
| 206 | | return Object.create(allocator, object_file, name); |
| 223 | return Object.create(allocator, object_file, name, object_file_size); |
| 207 | 224 | } |