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