M cmd/btinfo/main.ha => cmd/btinfo/main.ha +10 -0
@@ 1,3 1,5 @@
+use crypto::sha1;
+use encoding::hex;
use fmt;
use os;
use torrent;
@@ 12,10 14,18 @@ export fn main() void = {
defer torrent::torrent_free(tor);
fmt::printfln("announce: {}", tor.announce)!;
fmt::printfln("name: {}", tor.info.name)!;
+
match (tor.info.length) {
case void =>
yield;
case let z: size =>
fmt::printfln("bytes: {}", z)!;
};
+
+ for (let i = 0z; i < len(tor.info.pieces); i += sha1::SIZE) {
+ const piece = tor.info.pieces[i..i + sha1::SIZE];
+ fmt::printf("{}: ", i)!;
+ hex::encode(os::stdout, piece)!;
+ fmt::println()!;
+ };
};
M torrent/torrent.ha => torrent/torrent.ha +15 -42
@@ 47,7 47,6 @@ export fn read(in: io::handle) (*torrent | error) = {
t.announce = strings::dup(scan_str(dict, "announce")?);
const info = scan_dict(dict, "info")?;
- let nexpect = 0z;
for (true) {
const rec = match (bencode::dict_next(info)?) {
case let rec: (str, bencode::record) =>
@@ 58,51 57,27 @@ export fn read(in: io::handle) (*torrent | error) = {
switch (rec.0) {
case "name" =>
- if (!(rec.1 is []u8)) {
- return invalid;
- };
- const name = match (strings::try_fromutf8(rec.1 as []u8)) {
- case let s: str =>
- yield s;
- case =>
- return invalid;
- };
- t.info.name = strings::dup(name);
- nexpect += 1;
+ t.info.name = strings::dup(getstr(rec.1)?);
case "piece length" =>
- if (!(rec.1 is int)) {
- return invalid;
- };
- t.info.piecelen = rec.1 as int: size;
- nexpect += 1;
+ t.info.piecelen = getint(rec.1)?: size;
case "pieces" =>
- if (!(rec.1 is []u8)) {
- return invalid;
- };
- let pieces = rec.1: []u8;
+ let pieces = getbytes(rec.1)?;
if (len(pieces) % sha1::SIZE != 0) {
return invalid;
};
t.info.pieces = alloc(pieces...);
- nexpect += 1;
case "length" =>
- if (!(rec.1 is int)) {
- return invalid;
- };
- t.info.length = rec.1 as int: size;
- nexpect += 1;
+ t.info.length = getint(rec.1)?: size;
case "files" =>
- if (!(rec.1 is bencode::list_reader)) {
- return invalid;
- };
- scan_files(t, rec.1 as bencode::list_reader)?;
- nexpect += 1;
+ scan_files(t, getlist(rec.1)?)?;
case =>
yield;
};
};
- if (nexpect != 4) {
+ if (t.info.name == "" || t.info.piecelen == 0
+ || len(t.info.pieces) == 0
+ || (t.info.length is void && len(t.info.files) == 0)) {
return invalid;
};
@@ 120,26 95,24 @@ fn scan_files(t: *torrent, list: bencode::list_reader) (void | error) = {
return invalid;
};
- let file = file {
- length = scan_int(dict, "length")?: size,
- ...
- };
+ let file = file { ... };
+ file.length = scan_int(dict, "length")?: size;
+
let path = scan_list(dict, "path")?;
for (true) {
- const item = match (bencode::list_next(list)?) {
+ match (bencode::list_next(list)?) {
case io::EOF =>
return;
case let rec: []u8 =>
- yield match (strings::try_fromutf8(rec)) {
- case let s: str =>
- yield s;
+ match (strings::try_fromutf8(rec)) {
+ case let item: str =>
+ append(file.path, strings::dup(item));
case =>
return invalid;
};
case =>
return invalid;
};
- append(file.path, strings::dup(item));
};
append(t.info.files, file);
};
M torrent/util.ha => torrent/util.ha +37 -0
@@ 2,6 2,43 @@ use bencode;
use io;
use strings;
+fn getbytes(rec: bencode::record) ([]u8 | error) = {
+ match (rec) {
+ case let bytes: []u8 =>
+ return bytes;
+ case =>
+ return invalid;
+ };
+};
+
+fn getstr(rec: bencode::record) (str | error) = {
+ const bytes = getbytes(rec)?;
+ match (strings::try_fromutf8(bytes)) {
+ case let s: str =>
+ return s;
+ case =>
+ return invalid;
+ };
+};
+
+fn getint(rec: bencode::record) (int | error) = {
+ match (rec) {
+ case let i: int =>
+ return i;
+ case =>
+ return invalid;
+ };
+};
+
+fn getlist(rec: bencode::record) (bencode::list_reader | error) = {
+ match (rec) {
+ case let list: bencode::list_reader =>
+ return list;
+ case =>
+ return invalid;
+ };
+};
+
fn scan_bytes(dict: bencode::dict_reader, key: str) ([]u8 | error) = {
const val = scan_key(dict, key)?;
if (!(val is []u8)) {