Skip to content

Commit

Permalink
Fix Parsing Bug (#3)
Browse files Browse the repository at this point in the history
- Prepare for new features.
- Fixed bug that occurred when parsing a file that contained a `\n` after it's content.
- Improve argument parsing in `Foundationless`.
- Update license text.
Add Changelog.
  • Loading branch information
LebJe authored May 2, 2021
1 parent 5c85f97 commit 9305112
Show file tree
Hide file tree
Showing 16 changed files with 119 additions and 93 deletions.
26 changes: 13 additions & 13 deletions .swiftformat
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
--swiftversion 5.3
--binarygrouping none
--decimalgrouping none
--hexgrouping none
--indent tab
--indentcase true
--nospaceoperators "...,..<"
--octalgrouping none
--self insert
--semicolons never
--stripunusedargs unnamed-only
--wraparguments before-first
--wrapparameters before-first
--binarygrouping none
--decimalgrouping none
--hexgrouping none
--indent tab
--indentcase true
--nospaceoperators "...,..<"
--octalgrouping none
--self insert
--semicolons never
--stripunusedargs unnamed-only
--wraparguments before-first
--wrapparameters before-first
--disable trailingClosures, typeSugar
--header "Copyright (c) 2021 Jeff Lebrun \n\n \
Licensed under the MIT License. \n\n \
The full text license can be found in the file named LICENSE.
The full text of the license can be found in the file named LICENSE.
"
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]()

### Fixed

- Fixed a bug that occurred when parsing a file in an archive that contained a `\n` after it's content.

## [0.1.0](https://github.com/LebJe/ArArchiveKit/releases/tag/0.1.0) - 2021-04-09

### Added

- Support for reading and writing [BSD variant](https://www.freebsd.org/cgi/man.cgi?query=ar&sektion=5) `ar` archives.

## [0.0.2](https://github.com/LebJe/ArArchiveKit/releases/tag/0.0.2) - 2021-03-18

### Added

- Support for reading `ar` archives.

## [0.0.1](https://github.com/LebJe/ArArchiveKit/releases/tag/0.0.1) - 2021-03-17

### Added

- Support for creating `ar` archives.
12 changes: 6 additions & 6 deletions Examples/Foundationless/Sources/Foundationless/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

#if os(macOS)
import Darwin.C
Expand All @@ -28,7 +28,7 @@ func parseHelpFlag(_ s: String) -> Bool {
let usage = """
USAGE: \(CommandLine.arguments[0]) [--help, -h, -?] [-p] [-b] <file>
Reads the archive at `file` prints information about each file in the archive. Note that `-b` must come AFTER `-p`.
Reads the archive at `file` prints information about each file in the archive.
-h, --help, -? Prints this message.
-p Print the contents of the files in the archive.
Expand All @@ -44,11 +44,11 @@ func parseArgs() {
exit(1)
}

if CommandLine.arguments.count >= 3, CommandLine.arguments[2] == "-p" {
if CommandLine.arguments.firstIndex(of: "-p") != nil {
shouldPrintFile = true
}

if CommandLine.arguments.count >= 4, CommandLine.arguments[3] == "-b" {
if CommandLine.arguments.firstIndex(of: "-b") != nil {
printInBinary = true
}
}
Expand Down Expand Up @@ -81,9 +81,9 @@ for (header, file) in reader {
print("File Size: " + String(header.size))
print("File Modification Time: " + String(header.modificationTime))

print("Contents:\n")

if shouldPrintFile {
print("Contents:\n")

if printInBinary {
file.forEach({ print($0) })
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,13 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

import class Foundation.Bundle
import XCTest

final class FoundationlessTests: XCTestCase {
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.

// Some of the APIs that we use below are available in macOS 10.13 and above.
guard #available(macOS 10.13, *) else {
return
}

let fooBinary = productsDirectory.appendingPathComponent("Foundationless")

let process = Process()
process.executableURL = fooBinary

let pipe = Pipe()
process.standardOutput = pipe

try process.run()
process.waitUntilExit()

let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)

XCTAssertEqual(output, "Hello, world!\n")
}

/// Returns path to the built products directory.
var productsDirectory: URL {
#if os(macOS)
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
return bundle.bundleURL.deletingLastPathComponent()
}
fatalError("couldn't find the products directory")
#else
return Bundle.main.bundleURL
#endif
}
func testExample() throws {}

static var allTests = [
("testExample", testExample),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

import XCTest

Expand Down
2 changes: 1 addition & 1 deletion Examples/Foundationless/Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

import XCTest

Expand Down
11 changes: 7 additions & 4 deletions Sources/ArArchiveKit/ArArchiveReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

/// `ArArchiveReader` reads `ar` files.
public struct ArArchiveReader {
Expand Down Expand Up @@ -48,12 +48,15 @@ public struct ArArchiveReader {
while index < (self.data.count - 1), (index + (headerSize - 1)) < self.data.count - 1 {
var h = try self.parseHeader(bytes: Array(self.data[index...(index + headerSize - 1)]))

index += headerSize + 1
h.contentLocation = (index - 1) + (h.nameSize != nil ? h.nameSize! : 0)
h.contentLocation = (index + headerSize) + (h.nameSize != nil ? h.nameSize! : 0)

// Jump past the header.
index += headerSize

h.name = h.nameSize != nil ? String(Array(self.data[h.contentLocation - h.nameSize!..<h.contentLocation])) : h.name

index += h.size + (h.nameSize != nil ? h.nameSize! : 0)
// Jump past the content of the file.
index += (h.size % 2 != 0 ? h.size + 1 : h.size) + (h.nameSize != nil ? h.nameSize! : 0)

self.headers.append(h)
}
Expand Down
70 changes: 49 additions & 21 deletions Sources/ArArchiveKit/ArArchiveWriter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

/// `ArArchiveWriter` creates `ar` files.
public struct ArArchiveWriter {
Expand All @@ -11,6 +11,8 @@ public struct ArArchiveWriter {

public let variant: Variant

private var headers: [Header] = []

public init(variant: Variant = .common) {
self.variant = variant
self.addMagicBytes()
Expand All @@ -27,51 +29,77 @@ public struct ArArchiveWriter {
self.write(globalHeader.asciiArray)
}

private mutating func writeString(_ str: String, size: Int) {
private func stringToASCII(_ str: String, size: Int) -> [UInt8] {
var s = str

while s.count < size {
s = s + " "
}

self.bytes += s.asciiArray
return s.asciiArray
}

private mutating func writeInt<I: BinaryInteger>(_ int: I, size: Int, radix: Int? = nil, prefix: String? = nil) {
private func intToBytes<I: BinaryInteger>(_ int: I, size: Int, radix: Int? = nil, prefix: String? = nil) -> [UInt8] {
if let r = radix {
self.writeString((prefix != nil ? prefix! : "") + String(int, radix: r), size: size)
return self.stringToASCII((prefix != nil ? prefix! : "") + String(int, radix: r), size: size)
} else {
self.writeString((prefix != nil ? prefix! : "") + String(int), size: size)
return self.stringToASCII((prefix != nil ? prefix! : "") + String(int), size: size)
}
}

/// Adds a `Header` to the archive.
private mutating func addHeader(header: Header, contentSize: Int) {
private mutating func writeString(_ str: String, size: Int) {
self.bytes += self.stringToASCII(str, size: size)
}

private mutating func writeInt<I: BinaryInteger>(_ int: I, size: Int, radix: Int? = nil, prefix: String? = nil) {
self.bytes += self.intToBytes(int, size: size, radix: radix, prefix: prefix)
}

private func headerToBytes(header: Header, contentSize: Int) -> [UInt8] {
var header = header
var data: [UInt8] = []

switch self.variant {
case .common: self.writeString(header.name.truncate(length: 16), size: 16)
case .common: data += self.stringToASCII(header.name.truncate(length: 16), size: 16)
case .bsd:
self.writeString(header.name.count <= 16 && !header.name.contains(" ") ? header.name : "#1/\(header.name.count)", size: 16)
data += self.stringToASCII(header.name.count <= 16 && !header.name.contains(" ") ? header.name : "#1/\(header.name.count)", size: 16)
}

self.writeInt(header.modificationTime, size: 12, radix: 10)
self.writeInt(header.userID, size: 6, radix: 10)
self.writeInt(header.groupID, size: 6, radix: 10)
self.writeInt(header.mode, size: 8, radix: 8, prefix: "100")
data += self.intToBytes(header.modificationTime, size: 12, radix: 10)
data += self.intToBytes(header.userID, size: 6, radix: 10)
data += self.intToBytes(header.groupID, size: 6, radix: 10)
data += self.intToBytes(header.mode, size: 8, radix: 8, prefix: "100")

switch self.variant {
case .common:
self.writeInt(contentSize, size: 10, radix: 10)
self.writeString("`\n", size: 2)
data += self.intToBytes(contentSize, size: 10, radix: 10)
data += self.stringToASCII("`\n", size: 2)
case .bsd:
if header.name.count > 16 || header.name.contains(" ") {
self.writeInt(contentSize + header.name.count, size: 10, radix: 10)
self.writeString("`\n", size: 2)
self.writeString(header.name, size: header.name.count)
data += self.intToBytes(contentSize + header.name.count, size: 10, radix: 10)
data += self.stringToASCII("`\n", size: 2)
data += self.stringToASCII(header.name, size: header.name.count)
} else {
self.writeInt(contentSize, size: 10, radix: 10)
self.writeString("`\n", size: 2)
data += self.intToBytes(contentSize, size: 10, radix: 10)
data += self.stringToASCII("`\n", size: 2)
}
}

header.nameSize = header.name.count > 16 || header.name.contains(" ") ? header.name.count : nil
header.contentLocation = (self.bytes.endIndex - 1) + contentSize + (contentSize % 2 != 0 ? 1 : 0)

return data
}

/// Adds a `Header` to the archive.
private mutating func addHeader(header: Header, contentSize: Int) {
var header = header
header.startingLocation = self.bytes.endIndex - 1
self.bytes += self.headerToBytes(header: header, contentSize: contentSize)
header.endingLocation = (self.bytes.endIndex - 1) + contentSize
header.size = contentSize

self.headers.append(header)
}

/// Add a file to the archive.
Expand Down
2 changes: 1 addition & 1 deletion Sources/ArArchiveKit/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

let globalHeader = "!<arch>\n"
let headerSize = 60
2 changes: 1 addition & 1 deletion Sources/ArArchiveKit/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

public enum ArArchiveError: Error {
/// The archive was empty.
Expand Down
2 changes: 1 addition & 1 deletion Sources/ArArchiveKit/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

extension String {
var utf8Array: [UInt8] {
Expand Down
4 changes: 3 additions & 1 deletion Sources/ArArchiveKit/Header.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

/// The `ar` header.
///
Expand Down Expand Up @@ -33,6 +33,8 @@ public struct Header: Equatable {

internal var contentLocation: Int = 0
internal var nameSize: Int?
internal var startingLocation: Int?
internal var endingLocation: Int?

public init(
name: String,
Expand Down
2 changes: 1 addition & 1 deletion Sources/ArArchiveKit/Variant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

/// The different formats of the `ar` archive.
public enum Variant {
Expand Down
2 changes: 1 addition & 1 deletion Tests/ArArchiveKitTests/ArArchiveKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

@testable import ArArchiveKit
import Foundation
Expand Down
2 changes: 1 addition & 1 deletion Tests/ArArchiveKitTests/XCTestManifests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the MIT License.
//
// The full text license can be found in the file named LICENSE.
// The full text of the license can be found in the file named LICENSE.

import XCTest

Expand Down
Loading

0 comments on commit 9305112

Please sign in to comment.