Lindenii Project Forge
Stop using _is gihg
use bytes; use errors;
use encoding::utf8;
use strings; use strconv; // Author/committer identity and its associated timestamp // and timezone offset. export type ident = struct { name: []u8, email: []u8, when: i64, ofs: i32, }; // Frees resources associated with an [[ident]]. export fn ident_finish(p: ident) void = { free(p.name); free(p.email); }; // Parses an [[ident]] from its canonical byte-slice representation. fn parse_ident( line: []u8,
) (ident | errors::invalid | strconv::invalid | strconv::overflow | nomem) = {
) (ident | errors::invalid | strconv::invalid | strconv::overflow | utf8::invalid | nomem) = {
let mlt = bytes::index(line, '<'); if (mlt is void) { return errors::invalid; }; let lt = mlt: size; let mgt_rel = bytes::index(line[lt + 1z..], '>'); if (mgt_rel is void) { return errors::invalid; }; let gt_rel = mgt_rel: size; const gt = lt + 1z + gt_rel; const name_b = line[..lt]; const email_b = line[lt + 1z .. gt]; let rest = line[gt + 1z..]; if (len(rest) == 0 || rest[0] != ' ') { return errors::invalid; }; rest = rest[1..]; let msp = bytes::index(rest, ' '); if (msp is void) { return errors::invalid; }; let sp = msp: size;
const when_s = strings::fromutf8_unsafe(rest[..sp]);
const when_s = strings::fromutf8(rest[..sp])?;
const tz_b = rest[sp + 1z..]; if (len(tz_b) < 5) { return errors::invalid; }; const when = strconv::stoi64(when_s)?; let sign: i32 = 1; if (tz_b[0] == '-') { sign = -1; };
const hh = strconv::stou32(strings::fromutf8_unsafe(tz_b[1..3]), strconv::base::DEC)?; const mm = strconv::stou32(strings::fromutf8_unsafe(tz_b[3..5]), strconv::base::DEC)?;
const hh = strconv::stou32(strings::fromutf8(tz_b[1..3])?, strconv::base::DEC)?; const mm = strconv::stou32(strings::fromutf8(tz_b[3..5])?, strconv::base::DEC)?;
const mins: i32 = (hh: i32) * 60 + (mm: i32); const ofs: i32 = sign * mins; let name = alloc(name_b...)?; let email = alloc(email_b...)?; return ident { name = name, email = email, when = when, ofs = ofs }; };
use bytes; use compress::zlib; use encoding::utf8; use errors; use fmt; use fs; use io; use strconv; use strings;
use encoding::utf8;
// Find the path to a loose object with the given ID, // relative to the repository root. fn loose_relpath(id: oid) (str | nomem) = { const hex = oid_string(id)?; defer free(hex); const dir = strings::bytesub(hex, 0z, 2z)!; const file = strings::bytesub(hex, 2z, strings::end)!; return fmt::asprintf("objects/{}/{}", dir, file); }; // Reads a loose object from the repository by its ID. export fn read_loose( r: repo, id: oid, ) (object | fs::error | io::error | errors::invalid | strconv::invalid | strconv::overflow | nomem | utf8::invalid) = { const rel = loose_relpath(id)?; defer free(rel); const fh = fs::open(r.root, rel)?; defer io::close(fh)!; let zr = zlib::decompress(fh)?; defer io::close(&zr.vtable)!; let buf = io::drain(&zr.vtable)?; defer free(buf); let mnul = bytes::index(buf, 0u8); if (mnul is void) { return errors::invalid; }; let nul = mnul: size; const header = buf[..nul]; const body = buf[nul + 1z ..]; let msp = bytes::index(header, ' '); if (msp is void) { return errors::invalid; }; let sp = msp: size;
const ty = strings::fromutf8_unsafe(header[..sp]); const szs = strings::fromutf8_unsafe(header[sp + 1z ..]);
const ty = strings::fromutf8(header[..sp])?; const szs = strings::fromutf8(header[sp + 1z ..])?;
const expect = strconv::stoz(szs)?; if (expect != len(body)) { return errors::invalid; }; if (!verify_oid(buf, id)) { return errors::invalid; }; if (ty == "blob") { const b = parse_blob(body)?; return (b: object); } else if (ty == "tree") { const t = parse_tree(body)?; return (t: object); } else if (ty == "commit") { const c = parse_commit(body)?; return (c: object); } else if (ty == "tag") { const g = parse_tag(body)?; return (g: object); } else { return errors::invalid; }; }; // Reads a loose object from the repository by its ID, // returning its type and raw data. export fn read_loose_typed( r: repo, id: oid, ) ((objtype, []u8) | fs::error | io::error | errors::invalid | errors::noentry | strconv::invalid | strconv::overflow | nomem) = { const rel = loose_relpath(id)?; defer free(rel); let fh = fs::open(r.root, rel)?; defer io::close(fh)!; let zr = zlib::decompress(fh)?; defer io::close(&zr.vtable)!; let buf = io::drain(&zr.vtable)?; defer free(buf); let mnul = bytes::index(buf, 0u8); if (mnul is void) { return errors::invalid; }; let nul = mnul: size; const header = buf[..nul]; const body = buf[nul + 1z ..]; let msp = bytes::index(header, ' '); if (msp is void) { return errors::invalid; }; let sp = msp: size;
const ty = strings::fromutf8_unsafe(header[..sp]); const szs = strings::fromutf8_unsafe(header[sp + 1z ..]);
const ty = strings::fromutf8(header[..sp])?; const szs = strings::fromutf8(header[sp + 1z ..])?;
const expect = strconv::stoz(szs)?; if (expect != len(body)) { return errors::invalid; }; let code: objtype = objtype::OBJ_INVALID; if (ty == "blob") { code = objtype::OBJ_BLOB; } else if (ty == "tree") { code = objtype::OBJ_TREE; } else if (ty == "commit") { code = objtype::OBJ_COMMIT; } else if (ty == "tag") { code = objtype::OBJ_TAG; } else { return errors::invalid; }; let out = alloc(body...)?; return (code, out); };
use bytes;
use encoding::utf8;
use errors; use strconv; use strings; // A Git commit object. export type commit = struct { tree: oid, parents: []oid, author: ident, committer: ident, message: []u8, // other raw headers? }; // Frees resources associated with a [[commit]]. export fn commit_finish(c: commit) void = { free(c.parents); ident_finish(c.author); ident_finish(c.committer); free(c.message); }; // Parses a commit from its raw data and object ID. export fn parse_commit( body: []u8,
) (commit | errors::invalid | strconv::invalid | strconv::overflow | nomem) = {
) (commit | errors::invalid | strconv::invalid | strconv::overflow | utf8::invalid | nomem) = {
let c = commit { tree = [0...], parents = [], author = ident { name = [], email = [], when = 0, ofs = 0 }, committer = ident { name = [], email = [], when = 0, ofs = 0 }, message = [], }; let i = 0z; for (true) { let mrel = bytes::index(body[i..], '\n'); if (mrel is void) { return errors::invalid; }; let rel = mrel: size; const line = body[i .. i + rel]; if (len(line) == 0) { i += rel + 1z; break; }; if (bytes::hasprefix(line, strings::toutf8("tree "))) {
const hex = strings::fromutf8_unsafe(line[5..]);
const hex = strings::fromutf8(line[5..])?;
match (parse_oid(hex)) { case let o: oid => c.tree = o; case nomem => return nomem; case => return errors::invalid; }; } else if (bytes::hasprefix(line, strings::toutf8("parent "))) {
const hex = strings::fromutf8_unsafe(line[7..]);
const hex = strings::fromutf8(line[7..])?;
match (parse_oid(hex)) { case let o: oid => append(c.parents, o)!; case nomem => return nomem; case => return errors::invalid; }; } else if (bytes::hasprefix(line, strings::toutf8("author "))) { const per = parse_ident(line[7..])?; ident_finish(c.author); c.author = per; } else if (bytes::hasprefix(line, strings::toutf8("committer "))) { const per = parse_ident(line[10..])?; ident_finish(c.committer); c.committer = per; } else if ( bytes::hasprefix(line, strings::toutf8("gpgsig ")) || bytes::hasprefix(line, strings::toutf8("gpgsig-sha256 ")) ) { i += rel + 1z; for (true) { if (i >= len(body)) { return errors::invalid; }; let mnext = bytes::index(body[i..], '\n'); if (mnext is void) { return errors::invalid; }; let next = mnext: size; if (body[i] != ' ') { break; }; i += next + 1z; }; continue; }; i += rel + 1z; }; c.message = alloc(body[i..]...)?; return c; };
use bytes; use crypto::sha256;
use encoding::utf8;
use errors; use fs; use io; use strconv; use strings; // A Git tree object. export type tree = struct { entries: []tree_entry, }; // Frees resources associated with a [[tree]]. export fn tree_finish(t: tree) void = { for (let entry .. t.entries) { tree_entry_finish(entry); }; free(t.entries); }; // A single entry in a Git tree. In general, the oid // either refers to a blob (file) or another tree (directory). export type tree_entry = struct { mode: u32, name: []u8, oid: oid, }; // Frees resources associated with a [[tree_entry]]. export fn tree_entry_finish(te: tree_entry) void = { free(te.name); }; // Parses a tree from its raw data and object ID.
export fn parse_tree(body: []u8) (tree | errors::invalid | strconv::invalid | strconv::overflow | nomem) = {
export fn parse_tree(body: []u8) (tree | errors::invalid | strconv::invalid | strconv::overflow | utf8::invalid | nomem) = {
let entries: []tree_entry = []; let i = 0z; for (i < len(body)) { const sp = match (bytes::index(body[i..], ' ')) { case let j: size => yield j; case void => return errors::invalid; }; const mode_b = body[i .. i+sp]; i += sp + 1z; const nul = match (bytes::index(body[i..], 0u8)) { case let j: size => yield j; case void => return errors::invalid; }; const name_b = body[i .. i+nul]; i += nul + 1z; if (i + sha256::SZ > len(body)) return errors::invalid; let child: oid = [0...]; child[..] = body[i .. i+sha256::SZ]; i += sha256::SZ;
const mode_s = strings::fromutf8_unsafe(mode_b);
const mode_s = strings::fromutf8(mode_b)?;
const mode = strconv::stou32(mode_s, strconv::base::OCT)?; const name = alloc(name_b...)?; append(entries, tree_entry { mode = mode, name = name, oid = child })!; }; return tree { entries = entries }; }; // Looks up a tree entry by name. fn tree_entry_by_name_raw(t: *const tree, name: []const u8) (*const tree_entry | void) = { for (let i = 0z; i < len(t.entries); i += 1z) { if (bytes::equal(t.entries[i].name, name)) { return &t.entries[i]; }; }; return void; }; // Recursively looks up a tree or blob at the given path, export fn tree_at_path( r: repo, root: const tree, path: const []u8, ) (tree | blob | errors::invalid | fs::error | io::error | strconv::invalid | strconv::overflow | nomem) = { if (len(path) == 0) { return root; }; let owned_root = false; defer if (owned_root) { tree_finish(root); }; let i = 0z; for (i < len(path)) { let j = match (bytes::index(path[i..], '/')) { case let k: size => yield i + k; case void => yield len(path); }; if (j == i) { return errors::invalid; }; let comp = path[i..j]; let entp = tree_entry_by_name_raw(&root, comp); let ent: *const tree_entry = match (entp) { case let p: *const tree_entry => yield p; case void => return errors::invalid; }; let last = (j == len(path)); if (last) { match (read_object(r, ent.oid)) { case let t: tree => if (owned_root) { tree_finish(root); }; return t; case let b: blob => if (owned_root) { tree_finish(root); }; return b; case => if (owned_root) { tree_finish(root); }; return errors::invalid; }; } else { match (read_object(r, ent.oid)) { case let t: tree => if (owned_root) { tree_finish(root); }; root = t; owned_root = true; case => if (owned_root) { tree_finish(root); }; return errors::invalid; }; i = j + 1z; if (i >= len(path)) { return errors::invalid; }; }; }; return errors::invalid; };