Posted to tcl by dbohdan at Tue May 04 15:07:54 GMT 2021view pretty

#! /usr/bin/env tclsh

package require Tcl 8.7

namespace eval ::pkgfs {
    variable carts {
        tcllib-1.20 QmRjsBWcu4W78F19vkXeBgwY5m8fpD2A6SHeSCCsAnHh5B
    variable debug true
    variable version 0.1.0

namespace eval ::pkgfs::http {
    variable client {}

namespace eval ::pkgfs::ipfs {
    variable cacheDir [file normalize ~/.cache/pkgfs/]
    variable currentGateway 0
    variable gateways {

try {
    package require http
    package require tls

    http::register https 443 [list ::tls::socket -autoservername true]

    set ::pkgfs::http::client tcl-http
} on error e {
    try {
        exec curl --version
        set ::pkgfs::http::client curl-cli
    } on error _ {
        error {no usable HTTPS client}

proc ::pkgfs::grab {cart {tries 3}} {
    variable carts

    set pluggedIn false
    debug Grabbing cart $cart

    for {set i 1} {$i <= $tries} {incr i} {
        debug Attempt number $i

        try {
            set f [ipfs::cache [dict get $carts $cart]]
            debug Cart $cart is in $f
            plug-in $f
        } trap {PKGFS HTTP DOWNLOAD} e {
            debug HTTP client error: $e
        } trap {TCL ZIPFS} e {
            debug zipfs error: $e
            file delete $f
        } on ok _ {
            set pluggedIn true

    if {!$pluggedIn} {
        return \
            -code error \
            -errorcode {PKGFS GRAB} \
            [list failed to download and plug in cart $cart] \

    debug Plugged in $f

proc ::pkgfs::debug args {
    variable debug

    if {!$debug} return
    set caller [dict get [info frame -1] proc]
    puts stderr [format {PKGFS %-25s %s} $caller $args]

proc ::pkgfs::plug-in zip {
    set new [index-dirs [mount $zip]]
    lappend ::auto_path {*}$new

    return $new

proc ::pkgfs::mount zip {
    set root [zipfs root]
    set dest $root[file rootname [file tail $zip]]

    zipfs mount $dest $zip

    return $dest

proc ::pkgfs::index-dirs path {
    lmap f [zipfs list -glob $path*] {
        if {[file tail $f] ne {pkgIndex.tcl}} continue
        file dirname $f

proc ::pkgfs::ipfs::cache src {
    variable cacheDir

    file mkdir $cacheDir
    set dest [file join $cacheDir $src]

    if {[file exists $dest]} {
        ::pkgfs::debug Destination exists
        return $dest

    ::pkgfs::debug Downloading...
    download $src $dest

    return $dest

proc ::pkgfs::ipfs::download {src dest} {
    variable currentGateway
    variable gateways

    set gateway [lindex $gateways $currentGateway]
    set currentGateway [expr { ($currentGateway + 1) % [llength $gateways] }]
    ::pkgfs::debug Chose gateway $gateway
    ::pkgfs::http::download [format $gateway $src] $dest

proc ::pkgfs::http::download {url dest} {
    variable client

    try {
        $client $url $dest
    } on error {e opts} {
        dict set opts -errorcode {PKGFS HTTP DOWNLOAD}
        return -code error {*}$opts $e

proc ::pkgfs::http::curl-cli {url dest} {
    exec curl --fail --location --output $dest --silent $url

proc ::pkgfs::http::tcl-http {src dest} {
    try {
        set token [http::get $url]
        set data [http::data $token]

        set ch [open $dest wb]
        puts -nonewline $ch $data
    } finally {
        catch { http::cleanup $token }
        catch { close $ch }

pkgfs::grab tcllib-1.20
puts [package require aes]
puts [package require picoirc]

package provide pkgfs 0