├── .github └── workflows │ ├── modMetadata.yml │ └── moeCounter.yml ├── LICENSE ├── README.md ├── findURL.go ├── fsearch.go ├── go.mod ├── go.sum ├── helperFunctions.go ├── info.go ├── install.go ├── listBinaries.go ├── main.go ├── misc ├── assets │ ├── counter.svg │ └── pin.svg └── cmd │ └── modMetadata │ ├── Baseutils.bigdl_aarch64_arm64_Linux.json │ ├── Baseutils.bigdl_arm64_v8a_Android.json │ ├── Baseutils.bigdl_x86_64_Linux.json │ ├── Toolpacks.bigdl_aarch64_arm64_Linux.json │ ├── Toolpacks.bigdl_arm64_v8a_Android.json │ ├── Toolpacks.bigdl_x86_64_Linux.json │ ├── go.mod │ └── main.go ├── remove.go ├── run.go ├── stubdl └── update.go /.github/workflows/modMetadata.yml: -------------------------------------------------------------------------------- 1 | name: Modify Toolpacks & Baseutils metadata 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * MON,THU' # Runs bi-weekly 6 | workflow_dispatch: # Allows manual triggering 7 | 8 | jobs: 9 | update-modified-metadata: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v4 14 | 15 | - name: Setup Go 16 | uses: actions/setup-go@v5.0.2 17 | 18 | - name: Build the modMetadata command 19 | run: | 20 | cd misc/cmd/modMetadata 21 | if find . -maxdepth 1 -type f -name "*.json" | grep -q "."; then rm *.json; fi 22 | go build 23 | 24 | - name: Fetch and modify the Metadata 25 | run: | 26 | cd misc/cmd/modMetadata 27 | ./modMetadata 28 | rm ./modMetadata 29 | 30 | - name: Commit and push the updated metadata files 31 | run: | 32 | git config --local user.email "action@github.com" 33 | git config --local user.name "GitHub Action" 34 | git add ./misc/cmd/modMetadata 35 | git commit -m "[WEEKLY] Update Metadata files" 36 | git push 37 | -------------------------------------------------------------------------------- /.github/workflows/moeCounter.yml: -------------------------------------------------------------------------------- 1 | name: Update MoeCounter 2 | on: 3 | schedule: 4 | - cron: '0 0 * * 0' # Runs every Sunday at midnight UTC 5 | workflow_dispatch: # Allows manual triggering 6 | jobs: 7 | update-counter: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout repository 11 | uses: actions/checkout@v4 12 | 13 | - name: Get binary count 14 | id: get-listc 15 | run: | 16 | LISTC=$(wget -qO- "https://raw.githubusercontent.com/xplshn/bigdl/master/stubdl" | sh -s -- list | wc -l) 17 | echo "LISTC=$LISTC" >> $GITHUB_ENV 18 | 19 | - name: Download MoeCounter image 20 | run: | 21 | LISTC=$LISTC 22 | wget -O ./misc/assets/counter.svg "https://api.sefinek.net/api/v2/moecounter?number=${LISTC}&length=5&theme=gelbooru&pixelated=true" 23 | 24 | - name: Commit and push updated counter image 25 | run: | 26 | git config --local user.email "action@github.com" 27 | git config --local user.name "GitHub Action" 28 | git add ./misc/assets/counter.svg 29 | git commit -m "[WEEKLY] Update MoeCounter image" 30 | git push 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2024 xplshn@murena.io 2 | 3 | This software is licensed under the "Revised Anyone But Richard M. Stallman" (RABRMS) license, described below. 4 | 5 | ===--------------------------------------------------=== 6 | | The "Revised Anyone But Richard M. Stallman" license | 7 | ===--------------------------------------------------=== 8 | 9 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 10 | 11 | Conditions 12 | ---------- 13 | 14 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer. 15 | 16 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 17 | 18 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 19 | 20 | 4. You can relicense under any license that meets the first 3 conditions and isn't copylefted, as long as you aren't Richard M. Stallman. 21 | 22 | 5. Richard M. Stallman (the guy behind GNU, etc.) may not make use of, redistribute, nor relicense this program or any of its derivatives. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NOTICE: Development continues over at [`dbin`](https://github.com/xplshn/dbin) 2 | ## BigDL: Advanced Binary Management Tool 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/xplshn/bigdl)](https://goreportcard.com/report/github.com/xplshn/bigdl) 4 | [![License](https://img.shields.io/badge/license-%20RABRMS-bright_green)](https://github.com/xplshn/bigdl/blob/master/LICENSE) 5 | [![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/xplshn/bigdl?include_prereleases)](https://github.com/xplshn/bigdl/releases/latest) 6 | ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/xplshn/bigdl) 7 | 8 | 12 | 13 | BigDL is a sophisticated, Golang-based rewrite of the original [BDL](https://github.com/xplshn/Handyscripts/blob/master/bdl), it is like a package manager, but without the hassle of dependencies nor the bloat, every binary provided is statically linked. This tool is made to operate on Linux systems, BigDL is particularly well-suited for embedded systems, with support for both Amd64 AND Aarch64. Optionally, it works under Android too, but you'll have to set $INSTALL_DIR and $BIGDL_CACHE if you aren't running it under Termux, since depending the Android version and the ROM used, directories vary and the user's permission to modify them too. 14 | 15 | > Why? 16 | 17 | “I tend to think the drawbacks of dynamic linking outweigh the advantages for many (most?) applications.” – John Carmack 18 | 19 | > I've seen lots of package manager projects without "packages". What is different about this one? 20 | 21 | There are currently ![Current amount of binaries in the repos! x86_64](https://raw.githubusercontent.com/xplshn/bigdl/master/misc/assets/counter.svg) binaries in our repos. They are all statically linked. 22 | 23 | ### Features ![pin](https://raw.githubusercontent.com/xplshn/bigdl/master/misc/assets/pin.svg) 24 | 25 | ``` 26 | $ bigdl --help 27 | Usage: 28 | [-v|-h] [list|install|remove|update|run|info|search|tldr] <{args}> 29 | 30 | Options: 31 | -h, --help Show this help message 32 | -v, --version Show the version number 33 | 34 | Commands: 35 | list List all available binaries 36 | install, add Install a binary to $INSTALL_DIR 37 | remove, del Remove a binary from the $INSTALL_DIR 38 | update Update binaries, by checking their SHA against the repo's SHA 39 | run Run a binary from cache 40 | info Show information about a specific binary OR display installed binaries 41 | search Search for a binary - (not all binaries have metadata. Use list to see all binaries) 42 | tldr Show a brief description & usage examples for a given program/command. This is an alias equivalent to using "run" with "tlrc" as argument. 43 | ``` 44 | 45 | ### Examples 46 | ``` 47 | bigdl search editor 48 | bigdl install micro 49 | bigdl install lux kakoune aretext shfmt 50 | bigdl install --silent bed && echo "[bed] was installed to $INSTALL_DIR/bed" 51 | bigdl del bed 52 | bigdl del orbiton tgpt lux 53 | bigdl info 54 | bigdl info jq 55 | bigdl list --described 56 | bigdl tldr gum 57 | bigdl run --verbose curl -qsfSL "https://raw.githubusercontent.com/xplshn/bigdl/master/stubdl" | sh - 58 | bigdl run --silent elinks -no-home "https://fatbuffalo.neocities.org/def" 59 | bigdl run --transparent --silent micro ~/.profile 60 | bigdl run btop 61 | ``` 62 | 63 | #### What are these optional flags? ![pin](https://raw.githubusercontent.com/xplshn/bigdl/master/misc/assets/pin.svg) 64 | ##### Flags that correspond to the `run` functionality 65 | In the case of `--transparent`, it runs the program from $PATH and if it isn't available in the user's $PATH it will pull the binary from `bigdl`'s repos and run it from cache. 66 | In the case of `--silent`, it simply hides the progressbar and all optional messages (warnings) that `bigdl` can show, as oppossed to `--verbose`, which will always report if the binary is found on cache + the return code of the binary to be ran if it differs from 0. 67 | ##### Flags that correspond to the `install` functionality 68 | `--silent`, it hides the progressbar and doesn't print the installation message 69 | ##### `Update` arguments: 70 | Update can receive an optional list of specific binaries to update OR no arguments at all. When `update` receives no arguments it updates everything that is both found in the repos and in your `$INSTALL_DIR`. 71 | ##### Arguments of `info` 72 | When `info` is called with no arguments, it displays binaries which are part of the `list` and are also found on your `$INSTALL_DIR`. If `info` is called with a binary's name as argument, `info` will display as much information of it as is available. The "Size", "SHA256", "Version" fields may not match your local installation if the binary wasn't provided by `bigdl` or if it isn't up-to-date. 73 | ###### Example: 74 | ``` 75 | $ bigdl info micro 76 | Name: micro 77 | Description: A modern and intuitive terminal-based text editor 78 | Repo: https://github.com/zyedidia/micro 79 | Updated: 2024-05-22T20:21:10Z 80 | Version: v2.0.13 81 | Size: 11.81 MB 82 | Source: https://bin.ajam.dev/x86_64_Linux/micro 83 | SHA256: 697fb918c800071c4d1a853d515331a9a3f245bb8a7da1c6d3653737d17ce3c4 84 | ``` 85 | ##### Arguments of `list` 86 | `list` can receive the optional argument `--described`/`-d`. It will display all binaries that have a description in their metadata. 87 | ##### Arguments of `search` 88 | `search` can only receive ONE search term, if the name of a binary or a description of a binary contains the term, it is shown as a search result. 89 | `search` can optionally receive a `--limit` argument, which changes the limit on how many search results can be displayed (default is 90). 90 | 91 | ## Getting Started ![pin](https://raw.githubusercontent.com/xplshn/bigdl/master/misc/assets/pin.svg) 92 | 93 | To begin using BigDL, simply run one of these commands on your Linux system. No additional setup is required. You may also build the project using `go build or go install` 94 | #### Use without installing 95 | ``` 96 | wget -qO- "https://raw.githubusercontent.com/xplshn/bigdl/master/stubdl" | sh -s -- --help 97 | ``` 98 | ##### Install to `~/.local/bin` 99 | ``` 100 | wget -qO- "https://raw.githubusercontent.com/xplshn/bigdl/master/stubdl" | sh -s -- --install "$HOME/.local/bin/bigdl" 101 | ``` 102 | 103 | #### Example of one use case of bigdl | Inside of a SH script 104 | Whenever you want to pull a specific GNU coreutil, busybox, toybox, etc, insert a bash snippet, use a *fetch tool, etc, you can use bigdl for the job! There's also a `--transparent` flag for `run`, which will use the users' installed version of the program you want to run, and if it is not found in the `$PATH` bigdl will fetch it and run it from `/tmp/bigdl_cached`. 105 | ```sh 106 | system_info=$(wget -qO- "https://raw.githubusercontent.com/xplshn/bigdl/master/stubdl" | sh -s -- run --silent albafetch --no-logo - || curl -qsfSL "https://raw.githubusercontent.com/xplshn/bigdl/master/stubdl" | sh -s -- run --silent albafetch --no-logo -) 107 | ``` 108 | 109 | ### Where do these binaries come from? ![pin](https://raw.githubusercontent.com/xplshn/bigdl/master/misc/assets/pin.svg) 110 | - https://github.com/Azathothas/Toolpacks [https://bin.ajam.dev] [https://bin.ajam.dev/*/Baseutils/] 111 | >Hmm, can I add my own repos? 112 | 113 | Yes! Absolutely. The repo's URL's are declared in main.go, simply add another one if your repo is hosted at Github or your endpoint follows the same JSON format that Github's endpoint provides. You can also provide a repo URL in the same format that the [Toolpacks](https://github.com/Azathothas/Toolpacks) repo uses. 114 | 115 | >Good to hear, now... What about the so-called MetadataURLs? 116 | 117 | MetadataURLs provide info about the binaries, which is used to `search` and update `binaries`, also for the functionality of `info` in both of its use-cases (showing the binaries which were installed to $INSTALL_DIR from the [Toolpacks](https://github.com/Azathothas/Toolpacks) repo) and showing a binary's description, size, etc. 118 | 119 | ## NOTE 120 | A rewrite of `bigdl` from start to finish is underway. Applying the Data-Oriented paradigm, in a procedural/functional way, avoiding global variables and race conditions. (0.1/1) 121 | It will be release [One of These Days](https://music.youtube.com/watch?v=48PJGVf4xqk)... 122 | 123 | ### Libraries 124 | I am using these two libraries for `bigdl`: 125 | 1. https://github.com/schollz/progressbar 126 | 2. https://github.com/goccy/go-json 127 | 128 | ## Contributing 129 | Contributions are welcome! Whether you've found a bug, have a feature request, or wish to improve the documentation, your input is valuable. Fork the repository, make your changes, and submit a pull request. Together, we can make BigDL even more powerful and simpler. If you can provide repos that meet the requirements to add them to `bigdl`, I'd be grateful. 130 | Also, I need help optimizing the cyclomatic complexity of `bigdl`. 131 | 132 | ## License 133 | BigDL is licensed under the RABRMS License. This allows for the use, modification, and distribution of the software under certain conditions. For more details, please refer to the [LICENSE](LICENSE) file. This license is equivalent to the New or Revised BSD License. 134 | -------------------------------------------------------------------------------- /findURL.go: -------------------------------------------------------------------------------- 1 | // findURL.go // This file implements the findURL function //> 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | // findURLCommand returns the URL for the specified binary. We do not use info.go for this because unmarshalling such big files is slower than pinging to see which exists 10 | func findURLCommand(binaryName string) { 11 | url, err := findURL(binaryName) 12 | if err != nil { 13 | errorOut("error: %v\n", err) 14 | } 15 | 16 | fmt.Println(url) 17 | } 18 | 19 | // findURL fetches the URL for the specified binary. 20 | func findURL(binaryName string) (string, error) { 21 | iterations := 0 22 | for _, Repository := range Repositories { 23 | iterations++ 24 | url := fmt.Sprintf("%s%s", Repository, binaryName) 25 | fmt.Printf("\033[2K\r<%d/%d> | Working: Checking if \"%s\" is in the repos.", iterations, len(Repositories), binaryName) 26 | resp, err := http.Head(url) 27 | if err != nil { 28 | return "", err 29 | } 30 | 31 | if resp.StatusCode == http.StatusOK { 32 | fmt.Printf("\033[2K\r<%d/%d> | Found \"%s\" at %s", iterations, len(Repositories), binaryName, Repository) 33 | return url, nil 34 | } 35 | } 36 | 37 | fmt.Printf("\033[2K\r") 38 | return "", fmt.Errorf("Didn't find the SOURCE_URL for [%s]", binaryName) 39 | } 40 | -------------------------------------------------------------------------------- /fsearch.go: -------------------------------------------------------------------------------- 1 | // fsearch.go // this file implements the search option 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os/exec" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | // fSearch searches for binaries based on the given search term. 12 | func fSearch(searchTerm string, limit int) { 13 | type tBinary struct { 14 | Architecture string `json:"architecture"` 15 | Name string `json:"name"` 16 | Description string `json:"description"` 17 | } 18 | 19 | // Fetch metadata 20 | var binaries []tBinary 21 | err := fetchJSON(RNMetadataURL, &binaries) 22 | if err != nil { 23 | fmt.Println("Failed to fetch and decode binary information:", err) 24 | return 25 | } 26 | 27 | // Filter binaries based on the search term and architecture 28 | searchResultsSet := make(map[string]struct{}) 29 | for _, binary := range binaries { 30 | if strings.Contains(strings.ToLower(binary.Name), strings.ToLower(searchTerm)) || strings.Contains(strings.ToLower(binary.Description), strings.ToLower(searchTerm)) { 31 | ext := strings.ToLower(filepath.Ext(binary.Name)) 32 | base := filepath.Base(binary.Name) 33 | if _, excluded := excludedFileTypes[ext]; excluded { 34 | continue // Skip this binary if its extension is excluded 35 | } 36 | if _, excludedName := excludedFileNames[base]; excludedName { 37 | continue // Skip this binary if its name is excluded 38 | } 39 | if binary.Description != "" { 40 | entry := fmt.Sprintf("%s - %s", binary.Name, binary.Description) 41 | searchResultsSet[entry] = struct{}{} 42 | } 43 | } 44 | } 45 | 46 | // Check if no matching binaries found 47 | if len(searchResultsSet) == 0 { 48 | fmt.Printf("No matching binaries found for '%s'.\n", searchTerm) 49 | return 50 | } else if len(searchResultsSet) > limit { 51 | fmt.Printf("Too many matching binaries (+%d. [Use --limit before your query]) found for '%s'.\n", limit, searchTerm) 52 | return 53 | } 54 | 55 | // Convert set to slice for sorting 56 | var searchResults []string 57 | for entry := range searchResultsSet { 58 | searchResults = append(searchResults, entry) 59 | } 60 | 61 | // Sort the search results 62 | searchResults = sortBinaries(searchResults) 63 | 64 | // Check if the binary exists in the INSTALL_DIR and print results with installation state indicators 65 | for _, line := range searchResults { 66 | parts := strings.SplitN(line, " - ", 2) 67 | name := parts[0] 68 | description := parts[1] 69 | 70 | installPath := filepath.Join(InstallDir, name) 71 | cachedLocation, _ := ReturnCachedFile(name) 72 | 73 | prefix := "[-]" 74 | if fileExists(installPath) { 75 | prefix = "[i]" 76 | } else if path, err := exec.LookPath(name); err == nil && path != "" { 77 | prefix = "[\033[4mi\033[0m]" // Print [i],'i' is underlined 78 | } else if cachedLocation != "" && isExecutable(cachedLocation) { 79 | prefix = "[c]" 80 | } 81 | 82 | truncatePrintf("%s %s - %s ", prefix, name, description) 83 | fmt.Printf("\n") // Escape sequences are truncated too... 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/xplshn/bigdl 2 | 3 | go 1.22.0 4 | 5 | require ( 6 | github.com/goccy/go-json v0.10.3 7 | github.com/schollz/progressbar/v3 v3.14.4 8 | ) 9 | 10 | require ( 11 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect 12 | github.com/rivo/uniseg v0.4.7 // indirect 13 | golang.org/x/sys v0.21.0 // indirect 14 | golang.org/x/term v0.21.0 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= 5 | github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 6 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= 7 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 8 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= 9 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= 10 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 11 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 12 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 13 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 14 | github.com/schollz/progressbar/v3 v3.14.4 h1:W9ZrDSJk7eqmQhd3uxFNNcTr0QL+xuGNI9dEMrw0r74= 15 | github.com/schollz/progressbar/v3 v3.14.4/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI= 16 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 17 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 18 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 19 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 20 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 21 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 22 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 23 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 24 | golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= 25 | golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= 26 | -------------------------------------------------------------------------------- /helperFunctions.go: -------------------------------------------------------------------------------- 1 | // helperFunctions.go // This file contains commonly used functions //> 2 | package main 3 | 4 | import ( 5 | "context" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "os" 10 | "os/exec" 11 | "os/signal" 12 | "path/filepath" 13 | "sort" 14 | "strconv" 15 | "strings" 16 | "syscall" 17 | 18 | "github.com/goccy/go-json" 19 | "github.com/schollz/progressbar/v3" 20 | ) 21 | 22 | // signalHandler sets up a channel to listen for interrupt signals and returns a function that can be called to check if an interrupt has been received. 23 | func signalHandler(ctx context.Context, cancel context.CancelFunc) (func() bool, error) { 24 | sigChan := make(chan os.Signal, 1) 25 | signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) 26 | 27 | go func() { 28 | <-sigChan 29 | cancel() // Call the cancel function when an interrupt is received 30 | }() 31 | 32 | return func() bool { 33 | select { 34 | case <-ctx.Done(): 35 | return true 36 | default: 37 | return false 38 | } 39 | }, nil 40 | } 41 | 42 | // fetchBinaryFromURL fetches a binary from the given URL and saves it to the specified destination. 43 | func fetchBinaryFromURL(url, destination string) error { 44 | ctx, cancel := context.WithCancel(context.Background()) 45 | defer cancel() // Ensure the cancel function is called when the function returns 46 | 47 | // Set up signal handling 48 | interrupted, err := signalHandler(ctx, cancel) 49 | if err != nil { 50 | return fmt.Errorf("failed to set up signal handler: %v", err) 51 | } 52 | 53 | // Create a temporary directory if it doesn't exist 54 | if err := os.MkdirAll(TEMPDIR, 0o755); err != nil { 55 | return fmt.Errorf("failed to create temporary directory: %v", err) 56 | } 57 | 58 | // Create a temporary file to download the binary 59 | tempFile := filepath.Join(TEMPDIR, filepath.Base(destination)+".tmp") 60 | out, err := os.Create(tempFile) 61 | if err != nil { 62 | return fmt.Errorf("failed to create temporary file: %v", err) 63 | } 64 | defer out.Close() 65 | 66 | // Schedule the deletion of the temporary file 67 | defer func() { 68 | if err := os.Remove(tempFile); err != nil && !os.IsNotExist(err) { 69 | fmt.Printf("\r\033[Kfailed to remove temporary file: %v\n", err) 70 | } 71 | }() 72 | 73 | // Fetch the binary from the given URL 74 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) 75 | if err != nil { 76 | return fmt.Errorf("error creating request: %v", err) 77 | } 78 | 79 | // Ensure that redirects are followed 80 | client := &http.Client{ 81 | CheckRedirect: func(_ *http.Request, _ []*http.Request) error { 82 | return nil 83 | }, 84 | } 85 | 86 | resp, err := client.Do(req) 87 | if err != nil { 88 | return fmt.Errorf("failed to fetch binary from %s: %v", url, err) 89 | } 90 | defer resp.Body.Close() 91 | 92 | if resp.StatusCode != http.StatusOK { 93 | return fmt.Errorf("failed to fetch binary from %s. HTTP status code: %d", url, resp.StatusCode) 94 | } 95 | 96 | bar := spawnProgressBar(resp.ContentLength) 97 | 98 | // Write the binary to the temporary file with progress bar 99 | _, err = io.Copy(io.MultiWriter(out, bar), resp.Body) 100 | if err != nil { 101 | return fmt.Errorf("failed to write to temporary file: %v", err) 102 | } 103 | 104 | // Close the file before setting executable bit 105 | if err := out.Close(); err != nil { 106 | return fmt.Errorf("failed to close temporary file: %v", err) 107 | } 108 | 109 | // Use copyFile to move the binary to its destination 110 | if err := copyFile(tempFile, destination); err != nil { 111 | return fmt.Errorf("failed to move binary to destination: %v", err) 112 | } 113 | 114 | // Set executable bit immediately after copying 115 | if err := os.Chmod(destination, 0o755); err != nil { 116 | return fmt.Errorf("failed to set executable bit: %v", err) 117 | } 118 | 119 | // Check if the operation was interrupted 120 | if interrupted() { 121 | fmt.Println("\r\033[KDownload interrupted. Cleaning up...") 122 | // Ensure the temporary file is removed if the operation was interrupted 123 | if err := os.Remove(tempFile); err != nil && !os.IsNotExist(err) { 124 | fmt.Printf("failed to remove temporary file: %v\n", err) 125 | } 126 | } 127 | fmt.Print("\033[2K\r") // Clean the line 128 | return nil 129 | } 130 | 131 | // copyFile copies(removes original after copy) a file from src to dst 132 | func copyFile(src, dst string) error { 133 | // Check if the destination file already exists 134 | if fileExists(dst) { 135 | if err := os.Remove(dst); err != nil { 136 | return fmt.Errorf("%v", err) 137 | } 138 | } 139 | 140 | sourceFile, err := os.Open(src) 141 | if err != nil { 142 | return fmt.Errorf("failed to open source file: %v", err) 143 | } 144 | defer sourceFile.Close() 145 | 146 | destFile, err := os.Create(dst) 147 | if err != nil { 148 | return fmt.Errorf("failed to create destination file: %v", err) 149 | } 150 | 151 | _, err = io.Copy(destFile, sourceFile) 152 | if err != nil { 153 | destFile.Close() // Ensure the destination file is closed 154 | return fmt.Errorf("failed to copy file: %v", err) 155 | } 156 | 157 | if err := destFile.Close(); err != nil { 158 | return fmt.Errorf("failed to close destination file: %v", err) 159 | } 160 | 161 | // Remove the temporary file after copying 162 | if err := os.Remove(src); err != nil { 163 | return fmt.Errorf("failed to remove source file: %v", err) 164 | } 165 | 166 | return nil 167 | } 168 | 169 | func fetchJSON(url string, v interface{}) error { 170 | response, err := http.Get(url) 171 | if err != nil { 172 | return fmt.Errorf("error fetching from %s: %v", url, err) 173 | } 174 | defer response.Body.Close() 175 | 176 | body, err := io.ReadAll(response.Body) 177 | if err != nil { 178 | return fmt.Errorf("error reading from %s: %v", url, err) 179 | } 180 | 181 | if err := json.Unmarshal(body, v); err != nil { 182 | return fmt.Errorf("error decoding from %s: %v", url, err) 183 | } 184 | 185 | return nil 186 | } 187 | 188 | // removeDuplicates removes duplicate elements from the input slice. 189 | func removeDuplicates(input []string) []string { 190 | seen := make(map[string]bool) 191 | unique := []string{} 192 | if input != nil { 193 | for _, entry := range input { 194 | if _, value := seen[entry]; !value { 195 | seen[entry] = true 196 | unique = append(unique, entry) 197 | } 198 | } 199 | } else { 200 | unique = input 201 | } 202 | return unique 203 | } 204 | 205 | // sortBinaries sorts the input slice of binaries. 206 | func sortBinaries(binaries []string) []string { 207 | sort.Strings(binaries) 208 | return binaries 209 | } 210 | 211 | // fileExists checks if a file exists. 212 | func fileExists(filePath string) bool { 213 | _, err := os.Stat(filePath) 214 | return !os.IsNotExist(err) 215 | } 216 | 217 | // isExecutable checks if the file at the specified path is executable. 218 | func isExecutable(filePath string) bool { 219 | info, err := os.Stat(filePath) 220 | if err != nil { 221 | return false 222 | } 223 | return info.Mode().IsRegular() && (info.Mode().Perm()&0o111) != 0 224 | } 225 | 226 | // listFilesInDir lists all files in a directory 227 | func listFilesInDir(dir string) ([]string, error) { 228 | var files []string 229 | entries, err := os.ReadDir(dir) 230 | if err != nil { 231 | return nil, err 232 | } 233 | for _, entry := range entries { 234 | if !entry.IsDir() { 235 | files = append(files, dir+"/"+entry.Name()) 236 | } 237 | } 238 | return files, nil 239 | } 240 | 241 | func spawnProgressBar(contentLength int64) *progressbar.ProgressBar { 242 | if UseProgressBar { 243 | return progressbar.NewOptions(int(contentLength), 244 | progressbar.OptionClearOnFinish(), 245 | progressbar.OptionFullWidth(), 246 | progressbar.OptionShowBytes(true), 247 | progressbar.OptionSetTheme(progressbar.Theme{ 248 | Saucer: "=", 249 | SaucerHead: ">", 250 | SaucerPadding: " ", 251 | BarStart: "[", 252 | BarEnd: "]", 253 | }), 254 | ) 255 | } 256 | return progressbar.NewOptions(-1, 257 | progressbar.OptionSetWriter(io.Discard), 258 | progressbar.OptionSetVisibility(false), 259 | progressbar.OptionShowBytes(false), 260 | ) 261 | } 262 | 263 | // sanitizeString removes certain punctuation from the end of the string and converts it to lower case. 264 | func sanitizeString(s string) string { 265 | // Define the punctuation to remove 266 | punctuation := []string{".", " ", ",", "!", "?"} 267 | 268 | // Convert string to lower case 269 | s = strings.ToLower(s) 270 | 271 | // Remove specified punctuation from the end of the string 272 | for _, p := range punctuation { 273 | s = s[:len(s)-len(p)] 274 | } 275 | 276 | return s 277 | } 278 | 279 | // contanins will return true if the provided slice of []strings contains the word str 280 | func contains(slice []string, str string) bool { 281 | for _, v := range slice { 282 | if v == str { 283 | return true 284 | } 285 | } 286 | return false 287 | } 288 | 289 | // errorEncoder generates a unique error code based on the sum of ASCII values of the error message. 290 | func errorEncoder(format string, args ...interface{}) int { 291 | formattedErrorMessage := fmt.Sprintf(format, args...) 292 | 293 | var sum int 294 | for _, char := range formattedErrorMessage { 295 | sum += int(char) 296 | } 297 | errorCode := sum % 256 298 | fmt.Fprint(os.Stderr, formattedErrorMessage) 299 | return errorCode 300 | } 301 | 302 | // errorOut prints the error message to stderr and exits the program with the error code generated by errorEncoder. 303 | func errorOut(format string, args ...interface{}) { 304 | os.Exit(errorEncoder(format, args...)) 305 | } 306 | 307 | // GetTerminalWidth attempts to determine the width of the terminal. 308 | // It first tries using "stty size", then "tput cols", and finally falls back to 80 columns. 309 | func getTerminalWidth() int { 310 | // Try using stty size 311 | cmd := exec.Command("stty", "size") 312 | cmd.Stdin = os.Stdin 313 | out, err := cmd.Output() 314 | if err == nil { 315 | // stty size returns rows and columns 316 | parts := strings.Split(strings.TrimSpace(string(out)), " ") 317 | if len(parts) == 2 { 318 | width, _ := strconv.Atoi(parts[1]) 319 | return width 320 | } 321 | } 322 | 323 | // Fallback to tput cols 324 | cmd = exec.Command("tput", "cols") 325 | cmd.Stdin = os.Stdin 326 | out, err = cmd.Output() 327 | if err == nil { 328 | width, _ := strconv.Atoi(strings.TrimSpace(string(out))) 329 | return width 330 | } 331 | 332 | // Fallback to 80 columns 333 | return 80 334 | } 335 | 336 | // NOTE: \n will always get cut off when using a truncate function, this may also happen to other formatting options 337 | // truncateSprintf formats the string and truncates it if it exceeds the terminal width. 338 | func truncateSprintf(format string, a ...interface{}) string { 339 | // Format the string first 340 | formatted := fmt.Sprintf(format, a...) 341 | 342 | // Determine the truncation length & truncate the formatted string if it exceeds the available space 343 | availableSpace := getTerminalWidth() - len(indicator) 344 | if len(formatted) > availableSpace { 345 | formatted = formatted[:availableSpace] 346 | for strings.HasSuffix(formatted, ",") || strings.HasSuffix(formatted, ".") || strings.HasSuffix(formatted, " ") { 347 | formatted = formatted[:len(formatted)-1] 348 | } 349 | formatted = fmt.Sprintf("%s%s", formatted, indicator) // Add the dots. 350 | } 351 | 352 | return formatted 353 | } 354 | 355 | // truncatePrintf is a drop-in replacement for fmt.Printf that truncates the input string if it exceeds a certain length. 356 | func truncatePrintf(format string, a ...interface{}) (n int, err error) { 357 | if DisableTruncation { 358 | return fmt.Printf(format, a...) 359 | } 360 | if AddNewLineToTruncateFn { 361 | return fmt.Println(truncateSprintf(format, a...)) 362 | } 363 | return fmt.Print(truncateSprintf(format, a...)) 364 | } 365 | 366 | // validateProgramsFrom validates programs against the files in the specified directory against the remote binaries. 367 | func validateProgramsFrom(InstallDir string, programsToValidate []string) ([]string, error) { 368 | // Fetch the list of binaries from the remote source once 369 | remotePrograms, err := listBinaries() 370 | if err != nil { 371 | return nil, fmt.Errorf("failed to list remote binaries: %w", err) 372 | } 373 | 374 | // List files from the specified directory 375 | files, err := listFilesInDir(InstallDir) 376 | if err != nil { 377 | return nil, fmt.Errorf("failed to list files in %s: %w", InstallDir, err) 378 | } 379 | 380 | validPrograms := make([]string, 0) 381 | invalidPrograms := make([]string, 0) 382 | 383 | programsToValidate = removeDuplicates(programsToValidate) 384 | 385 | // If programsToValidate is nil, validate all programs in the install directory 386 | if programsToValidate == nil { 387 | for _, file := range files { 388 | // Extract the file name from the full path 389 | fileName := filepath.Base(file) 390 | if contains(remotePrograms, fileName) { 391 | validPrograms = append(validPrograms, fileName) 392 | } else { 393 | invalidPrograms = append(invalidPrograms, fileName) 394 | } 395 | } 396 | } else { 397 | // Only check the ones specified in programsToValidate 398 | for _, program := range programsToValidate { 399 | if contains(remotePrograms, program) { 400 | validPrograms = append(validPrograms, program) 401 | } else { 402 | invalidPrograms = append(invalidPrograms, program) 403 | } 404 | } 405 | } 406 | 407 | // Handle the list of programs received based on the last element 408 | // If programsToValidate is not nil, handle based on the last element 409 | return validPrograms, nil 410 | } 411 | -------------------------------------------------------------------------------- /info.go: -------------------------------------------------------------------------------- 1 | // info.go // This file implements binInfo, which `info` and `update` use //> 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | ) 7 | 8 | // BinaryInfo struct holds binary metadata used in main.go for the `info`, `update`, `list` functionality 9 | type BinaryInfo struct { 10 | Name string `json:"name"` 11 | Description string `json:"description"` 12 | Repo string `json:"repo_url"` 13 | ModTime string `json:"build_date"` 14 | Version string `json:"repo_version"` 15 | Updated string `json:"repo_updated"` 16 | Size string `json:"size"` 17 | Extras string `json:"extra_bins"` 18 | SHA256 string `json:"sha256"` 19 | Source string `json:"download_url"` 20 | } 21 | 22 | func findBinaryInfo(metadata []map[string]interface{}, binaryName string) (BinaryInfo, bool) { 23 | for _, binMap := range metadata { 24 | if name, ok := binMap["name"].(string); ok && name == binaryName { 25 | description, _ := binMap["description"].(string) 26 | repo, _ := binMap["repo_url"].(string) 27 | buildDate, _ := binMap["build_date"].(string) 28 | version, _ := binMap["repo_version"].(string) 29 | updated, _ := binMap["repo_updated"].(string) 30 | size, _ := binMap["size"].(string) 31 | extras, _ := binMap["extra_bins"].(string) 32 | sha256, _ := binMap["sha256"].(string) 33 | source, _ := binMap["download_url"].(string) 34 | 35 | return BinaryInfo{ 36 | Name: name, 37 | Description: description, 38 | Repo: repo, 39 | ModTime: buildDate, 40 | Version: version, 41 | Updated: updated, 42 | Size: size, 43 | Extras: extras, 44 | SHA256: sha256, 45 | Source: source, 46 | }, true 47 | } 48 | } 49 | return BinaryInfo{}, false 50 | } 51 | 52 | func getBinaryInfo(binaryName string) (*BinaryInfo, error) { 53 | var metadata []map[string]interface{} 54 | if err := fetchJSON(RNMetadataURL, &metadata); err != nil { 55 | return nil, err 56 | } 57 | 58 | binInfo, found := findBinaryInfo(metadata, binaryName) 59 | if !found { 60 | return nil, fmt.Errorf("error: info for the requested binary ('%s') not found in the metadata.json file", binaryName) 61 | } 62 | 63 | return &binInfo, nil 64 | } 65 | -------------------------------------------------------------------------------- /install.go: -------------------------------------------------------------------------------- 1 | // install.go // This file implements the install functionality // 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | func installCommand(silent bool, binaryNames string) error { 12 | // Disable the progressbar if the installation is to be performed silently 13 | if silent { 14 | UseProgressBar = false 15 | } 16 | binaries := strings.Fields(binaryNames) 17 | for _, binaryName := range binaries { 18 | // Extract the last part of the binaryName to use as the filename 19 | fileName := filepath.Base(binaryName) 20 | 21 | // Construct the installPath using the extracted filename 22 | installPath := filepath.Join(InstallDir, fileName) 23 | 24 | // Use ReturnCachedFile to check for a cached file 25 | if InstallUseCache { 26 | cachedFile, err := ReturnCachedFile(binaryName) 27 | if err == 0 { 28 | // If the cached file exists, use it 29 | if !silent { 30 | fmt.Printf("Using cached file: %s\n", cachedFile) 31 | } 32 | // Copy the cached file to the install path 33 | if err := copyFile(cachedFile, installPath); err != nil { 34 | return fmt.Errorf("error: Could not copy cached file: %v", err) 35 | } 36 | 37 | // Set executable bit immediately after copying 38 | if err := os.Chmod(installPath, 0o755); err != nil { 39 | return fmt.Errorf("failed to set executable bit: %v", err) 40 | } 41 | 42 | continue 43 | } 44 | } 45 | 46 | // If the cached file does not exist, download the binary 47 | url, err := findURL(binaryName) 48 | if err != nil { 49 | errorOut("%v\n", err) 50 | } 51 | 52 | if err := fetchBinaryFromURL(url, installPath); err != nil { 53 | return fmt.Errorf("%v", err) 54 | } 55 | 56 | if !silent { 57 | if InstallMessage != "disabled" { 58 | fmt.Print(InstallMessage) 59 | } else { 60 | fmt.Printf("Successfully created %s\n", installPath) 61 | } 62 | } 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /listBinaries.go: -------------------------------------------------------------------------------- 1 | // listBinaries.go // This file implements the listBinaries function //> 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "path/filepath" 7 | "strings" 8 | ) 9 | 10 | // listBinariesCommand fetches and lists binary names from the given URL. 11 | func listBinaries() ([]string, error) { 12 | var allBinaries []string 13 | var metadata []struct { 14 | Name string `json:"Name"` // Consider both "name" and "Name" fields 15 | NameAlt string `json:"name"` 16 | } 17 | // Fetch binaries from each metadata URL 18 | for _, url := range MetadataURLs { 19 | 20 | // Fetch metadata from the given URL 21 | if err := fetchJSON(url, &metadata); err != nil { 22 | return nil, fmt.Errorf("failed to fetch metadata from %s: %v", url, err) 23 | } 24 | 25 | // Extract binary names 26 | for _, item := range metadata { 27 | if item.Name != "" { 28 | allBinaries = append(allBinaries, item.Name) 29 | } 30 | if item.NameAlt != "" { 31 | allBinaries = append(allBinaries, item.NameAlt) 32 | } 33 | } 34 | } 35 | 36 | // Filter out excluded file types and file names 37 | var filteredBinaries []string 38 | for _, binary := range allBinaries { 39 | ext := strings.ToLower(filepath.Ext(binary)) 40 | base := filepath.Base(binary) 41 | if _, excluded := excludedFileTypes[ext]; !excluded { 42 | if _, excludedName := excludedFileNames[base]; !excludedName { 43 | filteredBinaries = append(filteredBinaries, binary) 44 | } 45 | } 46 | } 47 | 48 | // Remove duplicates based on their names 49 | uniqueBinaries := removeDuplicates(filteredBinaries) 50 | 51 | // Return the list of binaries 52 | return uniqueBinaries, nil 53 | } 54 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "runtime" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | var ( 14 | // Repositories contains all available repos - This variable is used by findURL.go 15 | Repositories []string 16 | // MetadataURLs are used for listing the binaries themselves. Not to be confused with R*MetadataURLs. 17 | MetadataURLs []string 18 | // RNMetadataURL should contain the JSON that describes available binaries for your architecture 19 | RNMetadataURL string 20 | // ValidatedArch is used in fsearch.go, info.go and main.go to determine which repos to use. 21 | ValidatedArch = [3]string{} 22 | // InstallDir holds the directory that shall be used for installing, removing, updating, listing with `info`. It takes the value of $INSTALL_DIR if it is set in the user's env, otherwise it is set to have a default value 23 | InstallDir = os.Getenv("INSTALL_DIR") 24 | // TEMPDIR will be used as the dir to download files to before moving them to a final destination AND as the place that will hold cached binaries downloaded by `run` 25 | TEMPDIR = os.Getenv("BIGDL_CACHEDIR") 26 | // InstallMessage will be printed when installCommand() succeeds 27 | InstallMessage = "disabled" 28 | // InstallUseCache determines if cached files should be used when requesting an install 29 | InstallUseCache = true 30 | // UseProgressBar determines if the progressbar is shown or not 31 | UseProgressBar = true 32 | // DisableTruncation determines if update.go, fsearch.go, etc, truncate their messages or not 33 | DisableTruncation = false 34 | // Always adds a NEWLINE to text truncated by the truncateSprintf/truncatePrintf function 35 | AddNewLineToTruncateFn = false 36 | ) 37 | 38 | const ( 39 | VERSION = "1.6.9" // VERSION to be displayed 40 | usagePage = " [-v|-h] [list|install|remove|update|run|info|search|tldr] <-args->" // usagePage to be shown 41 | // Truncation indicator 42 | indicator = "...>" 43 | // MaxCacheSize is the limit of binaries which can be stored at TEMP_DIR 44 | MaxCacheSize = 10 45 | // BinariesToDelete - Once the cache is filled - The programs populate the list of binaries to be removed in order of least used. This variable sets the amount of binaries that should be deleted 46 | BinariesToDelete = 5 47 | ) 48 | 49 | // Exclude specified file types and file names, these shall not appear in Lists nor in the Search Results 50 | var excludedFileTypes = map[string]struct{}{ 51 | ".7z": {}, 52 | ".bz2": {}, 53 | ".json": {}, 54 | ".gz": {}, 55 | ".md": {}, 56 | ".txt": {}, 57 | ".tar": {}, 58 | ".zip": {}, 59 | ".cfg": {}, 60 | ".dir": {}, 61 | ".test": {}, 62 | } 63 | 64 | var excludedFileNames = map[string]struct{}{ 65 | "TEST": {}, 66 | "LICENSE": {}, 67 | "experimentalBinaries_dir": {}, 68 | "bundles_dir": {}, 69 | "blobs_dir": {}, 70 | "robotstxt": {}, 71 | "bdl.sh": {}, 72 | // Because the repo contains duplicated files. And I do not manage the repo nor plan to implement sha256 filtering : 73 | "uroot": {}, 74 | "uroot-busybox": {}, 75 | "gobusybox": {}, 76 | "sysinfo-collector": {}, 77 | "neofetch": {}, 78 | "sh": {}, // Because in the repo, it is a duplicate of bash and not a POSIX implementation nor the original Thompshon Shell 79 | } 80 | 81 | func init() { 82 | if InstallDir == "" { 83 | homeDir, err := os.UserHomeDir() 84 | if err != nil { 85 | errorOut("error: Failed to get user's Home directory. Maybe set $BIGDL_CACHEDIR? %v\n", err) 86 | os.Exit(1) 87 | } 88 | InstallDir = filepath.Join(homeDir, ".local", "bin") 89 | } 90 | if TEMPDIR == "" { 91 | cacheDir, err := os.UserCacheDir() 92 | if err != nil { 93 | errorOut("error: Failed to get user's Cache directory. Maybe set $BIGDL_CACHEDIR? %v\n", err) 94 | os.Exit(1) 95 | } 96 | TEMPDIR = filepath.Join(cacheDir, "bigdl_cache") 97 | } 98 | if os.Getenv("BIGDL_TRUNCATION") == "0" { 99 | DisableTruncation = true 100 | } 101 | if os.Getenv("BIGDL_ADDNEWLINE") == "1" { 102 | AddNewLineToTruncateFn = true 103 | } 104 | if os.Getenv("BIGDL_PRBAR") == "0" { 105 | UseProgressBar = false 106 | } 107 | 108 | // The repos are a mess. So we need to do this. Sorry 109 | arch := runtime.GOARCH + "_" + runtime.GOOS 110 | switch arch { 111 | case "amd64_linux": 112 | ValidatedArch = [3]string{"x86_64_Linux", "x86_64", "x86_64-Linux"} 113 | case "arm64_linux": 114 | ValidatedArch = [3]string{"aarch64_arm64_Linux", "aarch64_arm64", "aarch64-Linux"} 115 | case "arm64_android": 116 | ValidatedArch = [3]string{"arm64_v8a_Android", "arm64_v8a_Android", "arm64-v8a-Android"} 117 | // case "amd64_windows": // not yet supported. Not sure if it will ever be. 118 | // ValidatedArch = [3]string{"x64_Windows", "x64_Windows", "AMD64-Windows_NT"} 119 | default: 120 | fmt.Println("Unsupported architecture:", arch) 121 | os.Exit(1) 122 | } 123 | arch = ValidatedArch[0] 124 | Repositories = append(Repositories, "https://bin.ajam.dev/"+arch+"/") 125 | Repositories = append(Repositories, "https://bin.ajam.dev/"+arch+"/Baseutils/") 126 | //Repositories = append(Repositories, "https://raw.githubusercontent.com/xplshn/Handyscripts/master/") 127 | // Binaries that are available in the Repositories but aren't described in any MetadataURLs will not be updated, nor listed with `info` nor `list` 128 | RNMetadataURL = "https://bin.ajam.dev/" + arch + "/METADATA.json" // RNMetadataURL is the file which contains a concatenation of all metadata in the different repos, this one also contains sha256 checksums 129 | MetadataURLs = append(MetadataURLs, "https://bin.ajam.dev/"+arch+"/METADATA.json") 130 | MetadataURLs = append(MetadataURLs, "https://bin.ajam.dev/"+arch+"/Baseutils/METADATA.json") 131 | //MetadataURLs = append(MetadataURLs, "https://api.github.com/repos/xplshn/Handyscripts/contents") 132 | } 133 | 134 | func printHelp() { 135 | helpMessage := "Usage:\n" + usagePage + ` 136 | 137 | Options: 138 | -h, --help Show this help message 139 | -v, --version Show the version number 140 | 141 | Commands: 142 | list List all available binaries 143 | install, add Install a binary 144 | remove, del Remove a binary 145 | update Update binaries, by checking their SHA against the repo's SHA 146 | run Run a specified binary from cache 147 | info Show information about a specific binary OR display installed binaries 148 | search Search for a binary - (not all binaries have metadata. Use list to see all binaries) 149 | tldr Equivalent to "run --transparent --verbose tlrc" as argument 150 | 151 | Variables: 152 | BIGDL_PRBAR If present, and set to ZERO (0), the download progressbar will be disabled 153 | BIGDL_TRUNCATION If present, and set to ZERO (0), string truncation will be disabled 154 | BIGDL_ADDNEWLINE If present, and set to ONE (1), truncated strings will always be on a new line 155 | BIGDL_CACHEDIR If present, it must contain a valid directory 156 | INSTALL_DIR If present, it must contain a valid directory 157 | 158 | Examples: 159 | bigdl search editor 160 | bigdl install micro 161 | bigdl install lux kakoune aretext shfmt 162 | bigdl install --silent bed && echo "[bed] was installed to $INSTALL_DIR/bed" 163 | bigdl del bed 164 | bigdl del orbiton tgpt lux 165 | bigdl info 166 | bigdl info jq 167 | bigdl list --described 168 | bigdl tldr gum 169 | bigdl run --verbose curl -qsfSL "https://raw.githubusercontent.com/xplshn/bigdl/master/stubdl" | sh - 170 | bigdl run --silent elinks -no-home "https://fatbuffalo.neocities.org/def" 171 | bigdl run --transparent --silent micro ~/.profile 172 | bigdl run btop 173 | 174 | Version: ` + VERSION 175 | 176 | fmt.Println(helpMessage) 177 | } 178 | 179 | func main() { 180 | errorOutInsufficientArgs := func() { errorOut("Error: Insufficient parameters\n") } 181 | version := flag.Bool("v", false, "Show the version number") 182 | versionLong := flag.Bool("version", false, "Show the version number") 183 | 184 | flag.Usage = printHelp 185 | flag.Parse() 186 | 187 | if *version || *versionLong { 188 | errorOut("bigdl %s\n", VERSION) 189 | } 190 | 191 | if flag.NArg() < 1 { 192 | errorOut(" bigdl:%s\n", usagePage) 193 | } 194 | 195 | if err := os.MkdirAll(InstallDir, os.ModePerm); err != nil { 196 | fmt.Fprintf(os.Stderr, "Error: Failed to get user's Home directory. %v\n", err) 197 | os.Exit(1) 198 | } 199 | 200 | switch flag.Arg(0) { 201 | case "find_url": 202 | binaryName := flag.Arg(1) 203 | if binaryName == "" { 204 | fmt.Println("Usage: bigdl find_url [binary]") 205 | errorOutInsufficientArgs() 206 | } 207 | findURLCommand(binaryName) 208 | case "list": 209 | if len(os.Args) == 3 { 210 | if os.Args[2] == "--described" || os.Args[2] == "-d" { 211 | // Call fSearch with an empty query and a large limit to list all described binaries 212 | fSearch("", 99999) 213 | } else { 214 | errorOut("bigdl: Unknown command.\n") 215 | } 216 | } else { 217 | binaries, err := listBinaries() 218 | if err != nil { 219 | fmt.Println("Error listing binaries:", err) 220 | os.Exit(1) 221 | } 222 | for _, binary := range binaries { 223 | fmt.Println(binary) 224 | } 225 | } 226 | case "install", "add": 227 | if flag.NArg() < 2 { 228 | fmt.Printf("Usage: bigdl %s <--silent> [binar|y|ies]\n", flag.Arg(0)) 229 | os.Exit(1) 230 | } 231 | 232 | // Join the binary names into a single string separated by spaces 233 | binaries := strings.Join(flag.Args()[1:], " ") 234 | silent := false 235 | if flag.Arg(1) == "--silent" { 236 | silent = true 237 | // Skip the "--silent" flag when joining the binary names 238 | binaries = strings.Join(flag.Args()[2:], " ") 239 | } 240 | 241 | err := installCommand(silent, binaries) 242 | if err != nil { 243 | fmt.Printf("Installation failed: %v\n", err) 244 | os.Exit(1) 245 | } 246 | case "remove", "del": 247 | if flag.NArg() < 2 { 248 | fmt.Printf("Usage: bigdl %s [binar|y|ies]\n", flag.Arg(0)) 249 | errorOutInsufficientArgs() 250 | } 251 | remove(flag.Args()[1:]) 252 | case "run": 253 | if flag.NArg() < 2 { 254 | fmt.Println("Usage: bigdl run <--verbose, --silent, --transparent> [binary] ") 255 | errorOutInsufficientArgs() 256 | } 257 | RunFromCache(flag.Arg(1), flag.Args()[2:]) 258 | case "tldr": 259 | args := append([]string{"--transparent", "--verbose", "tlrc"}, flag.Args()[1:]...) // UGLY! 260 | RunFromCache(args[0], args[1:]) 261 | case "info": 262 | binaryName := flag.Arg(1) 263 | if len(os.Args) < 3 { 264 | installedPrograms, err := validateProgramsFrom(InstallDir, nil) 265 | if err != nil { 266 | fmt.Println("Error validating programs:", err) 267 | return 268 | } 269 | for _, program := range installedPrograms { 270 | fmt.Println(program) 271 | } 272 | } else { 273 | binaryInfo, err := getBinaryInfo(binaryName) 274 | if err != nil { 275 | errorOut("%v\n", err) 276 | } 277 | fmt.Printf("Name: %s\n", binaryInfo.Name) 278 | if binaryInfo.Description != "" { 279 | fmt.Printf("Description: %s\n", binaryInfo.Description) 280 | } 281 | if binaryInfo.Repo != "" { 282 | fmt.Printf("Repo: %s\n", binaryInfo.Repo) 283 | } 284 | if binaryInfo.Updated != "" { 285 | fmt.Printf("Updated: %s\n", binaryInfo.Updated) 286 | } 287 | if binaryInfo.Version != "" { 288 | fmt.Printf("Version: %s\n", binaryInfo.Version) 289 | } 290 | if binaryInfo.Size != "" { 291 | fmt.Printf("Size: %s\n", binaryInfo.Size) 292 | } 293 | if binaryInfo.Source != "" { // if binaryInfo.Extras != "" { 294 | fmt.Printf("Source: %s\n", binaryInfo.Source) 295 | } 296 | if binaryInfo.SHA256 != "" { 297 | fmt.Printf("SHA256: %s\n", binaryInfo.SHA256) 298 | } 299 | } 300 | case "search": 301 | limit := 90 302 | queryIndex := 2 303 | 304 | if len(os.Args) < queryIndex+1 { 305 | fmt.Println("Usage: bigdl search <--limit||-l [int]> [query]") 306 | os.Exit(1) 307 | } 308 | 309 | if len(os.Args) > 2 && os.Args[queryIndex] == "--limit" || os.Args[queryIndex] == "-l" { 310 | if len(os.Args) > queryIndex+1 { 311 | var err error 312 | limit, err = strconv.Atoi(os.Args[queryIndex+1]) 313 | if err != nil { 314 | errorOut("Error: 'limit' value is not an int.\n") 315 | } 316 | queryIndex += 2 317 | } else { 318 | errorOut("Error: Missing 'limit' value.\n") 319 | } 320 | } 321 | 322 | query := os.Args[queryIndex] 323 | fSearch(query, limit) 324 | case "update": 325 | var programsToUpdate []string 326 | if len(os.Args) > 2 { 327 | programsToUpdate = os.Args[2:] 328 | } 329 | update(programsToUpdate) 330 | default: 331 | errorOut("bigdl: Unknown command.\n") 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /misc/assets/counter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Moecounter.js - api.sefinek.net 4 | 5 | -------------------------------------------------------------------------------- /misc/assets/pin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /misc/cmd/modMetadata/Toolpacks.bigdl_arm64_v8a_Android.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "act", 4 | "description": "Run your GitHub Actions locally", 5 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/act", 6 | "size": "19.31 MB", 7 | "b3sum": "67199f31ff589e05b44fb23a1eb01e2605fa65f17105bb2db14cb8fbdaf446b4", 8 | "sha256": "946b505fbf0dea70ef9a4e276900ec7c91fbdc49056c15ec8c37e53506b7618b", 9 | "build_date": "2024-08-18T18:46:12", 10 | "repo_url": "https://github.com/nektos/act", 11 | "repo_author": "nektos", 12 | "repo_info": "Run your GitHub Actions locally 🚀", 13 | "repo_updated": "2024-08-19T02:56:30Z", 14 | "repo_released": "2024-08-01T02:26:58Z", 15 | "repo_version": "v0.2.65", 16 | "repo_stars": "53379", 17 | "repo_language": "Go", 18 | "repo_license": "MIT License", 19 | "repo_topics": "ci, devops, github-actions, golang", 20 | "web_url": "https://github.com/nektos/act" 21 | }, 22 | { 23 | "name": "actionlint", 24 | "description": "Static checker for GitHub Actions workflow files", 25 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/actionlint", 26 | "size": "5.38 MB", 27 | "b3sum": "747a57a1bc57d6348b2d9af55c0106696922cbcbdbbae25e8f78a0c3fd769f72", 28 | "sha256": "c3216fc038dd0dfb3e88d46ab061129ef56f45283c0feaa1e6498d40514bc463", 29 | "build_date": "2024-07-23T10:24:52", 30 | "repo_url": "https://github.com/rhysd/actionlint", 31 | "repo_author": "rhysd", 32 | "repo_info": ":octocat: Static checker for GitHub Actions workflow files", 33 | "repo_updated": "2024-07-02T09:12:41Z", 34 | "repo_released": "2024-05-28T11:50:24Z", 35 | "repo_version": "v1.7.1", 36 | "repo_stars": "2644", 37 | "repo_language": "Go", 38 | "repo_license": "MIT License", 39 | "repo_topics": "actions, ci, github-actions, lint, linter", 40 | "web_url": "https://github.com/rhysd/actionlint" 41 | }, 42 | { 43 | "name": "adig", 44 | "description": "A command-line tool that allows you to perform DNS lookups from the command line", 45 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/adig", 46 | "size": "168.25 kB", 47 | "b3sum": "cf41b1b7dff2f3023157317b902cc0d106fd940e7eee1b6a7074cd2c61d3338d", 48 | "sha256": "f61bccac6dbbfdac8c35df4780d685d0f1f5476cce9c4eaf6bb20881170e2204", 49 | "build_date": "2024-08-20T18:49:25", 50 | "repo_url": "https://github.com/c-ares/c-ares", 51 | "repo_author": "c-ares", 52 | "repo_info": "A C library for asynchronous DNS requests", 53 | "repo_updated": "2024-08-20T16:14:27Z", 54 | "repo_released": "2024-08-02T12:50:57Z", 55 | "repo_version": "v1.33.0", 56 | "repo_stars": "1829", 57 | "repo_language": "C", 58 | "repo_license": "MIT License", 59 | "repo_topics": "async, c, dns, dns-queries, library, resolver", 60 | "web_url": "https://github.com/c-ares/c-ares" 61 | }, 62 | { 63 | "name": "anew-rs", 64 | "description": "A fast, simple, recursive content discovery tool written in Rust.", 65 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/anew-rs", 66 | "size": "939.71 kB", 67 | "b3sum": "d4a965a8c459e7b2d526a37b6f5cb41d2c8d774d2acb5a93a2fb0728be5bf6b1", 68 | "sha256": "379d7ef24740d152cd8f89c8ceee972ad49f40e51719bd5be195a7f2a1d47b27", 69 | "build_date": "2024-08-18T18:48:19", 70 | "repo_url": "https://github.com/zer0yu/anew", 71 | "repo_author": "zer0yu", 72 | "repo_info": "A tool for adding new lines to files, skipping duplicates and written in Rust!", 73 | "repo_updated": "2024-05-08T12:29:15Z", 74 | "repo_released": "2023-11-23T16:11:10Z", 75 | "repo_version": "v0.1.0", 76 | "repo_stars": "12", 77 | "repo_language": "Rust", 78 | "repo_license": "MIT License", 79 | "web_url": "https://github.com/zer0yu/anew" 80 | }, 81 | { 82 | "name": "axel", 83 | "description": "A lightweight download accelerator", 84 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/axel", 85 | "size": "3.76 MB", 86 | "b3sum": "6c521b63eb7b67a368924461193a24f76f82253dad2b7370039a00960a07a6dd", 87 | "sha256": "7fc0fe7ffe73b8304fe52fd8250b53ef59fe1fdea7ea9ecedac98543ed0e389c", 88 | "build_date": "2024-08-20T18:57:56", 89 | "repo_url": "https://github.com/axel/axel", 90 | "web_url": "https://github.com/axel/axel" 91 | }, 92 | { 93 | "name": "bash", 94 | "description": "GNU Bourne-Again Shell, the de facto standard shell on Linux", 95 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/bash", 96 | "size": "1.44 MB", 97 | "b3sum": "e1b1b406bac299fde0eab2dc4f1d9941b4f1ba81442be1cd03a54b10d3d5016a", 98 | "sha256": "b0995152b4d1a9da47bd7665330a5bf8f703d620f4cc402c968b4e5b9968b0d0", 99 | "build_date": "2024-07-23T10:35:16", 100 | "web_url": "https://www.bash.ws/" 101 | }, 102 | { 103 | "name": "bigdl", 104 | "description": "📦 Poor mans package manager.", 105 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/bigdl", 106 | "size": "6.69 MB", 107 | "b3sum": "badcb9a7cbdf13032460336c043072673877603c054c9f4470be05a8a8b71c2e", 108 | "sha256": "a54f0f5573da328f199b7f9a22084259a3fc04d14e3af525d2dddd621e92acf8", 109 | "build_date": "2024-08-20T19:01:27", 110 | "repo_url": "https://github.com/xplshn/bigdl", 111 | "repo_author": "xplshn", 112 | "repo_info": "📦 Poor mans package manager. +2180 statically linked binaries in the repos!", 113 | "repo_updated": "2024-08-19T01:09:37Z", 114 | "repo_released": "2024-07-08T16:02:44Z", 115 | "repo_version": "1.6.9", 116 | "repo_stars": "12", 117 | "repo_language": "Go", 118 | "repo_license": "Other", 119 | "repo_topics": "binary-manager, cli, dependency-checker, fast, go, golang, no-config, package-management, package-manager, package-manager-tool, runner, standalone, static, static-binaries, static-binary", 120 | "web_url": "https://github.com/xplshn/bigdl" 121 | }, 122 | { 123 | "name": "wget", 124 | "description": "A Software Suite that provides Several Unix Utilities in a single Executable", 125 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/wget", 126 | "size": "5.81 MB", 127 | "b3sum": "5746d29935890e8624473a66e28cd28576f975d3a7c46ac7cdbe150798b22bbd", 128 | "sha256": "2712d5437267cee5ff3df91e6bbfa3e9347625bd217315beac660bdfb63ceb26", 129 | "build_date": "2024-08-20T19:29:04", 130 | "web_url": "https://www.busybox.net/", 131 | "extra_bins": "[,[[,acpid,add_shell,addgroup,adduser,adjtimex,arch,arp,arping,ascii,ash,awk,base32,base64,basename,bb_arch,bb_sysctl,bc,beep,blkdiscard,blkid,blockdev,bootchartd,brctl,bunzip2,busybox,bzcat,bzip2,cal,cat,chat,chattr,chgrp,chmod,chown,chpasswd,chpst,chroot,chrt,chvt,cksum,clear,cmp,comm,conspy,cp,cpio,crc32,crond,crontab,cryptpw,cttyhack,cut,date,dc,dd,deallocvt,delgroup,deluser,depmod,devmem,df,dhcprelay,diff,dirname,dmesg,dnsd,dnsdomainname,dos2unix,dpkg,dpkg_deb,du,dumpkmap,dumpleases,echo,ed,egrep,eject,env,envdir,envuidgid,ether_wake,expand,expr,factor,fakeidentd,fallocate,false,fatattr,fbset,fbsplash,fdflush,fdformat,fdisk,fgconsole,fgrep,find,findfs,flock,fold,free,freeramdisk,fsck,fsck_minix,fsfreeze,fstrim,fsync,ftpd,ftpget,ftpput,fuser,getfattr,getopt,getty,grep,groups,gunzip,gzip,halt,hd,hdparm,head,hexdump,hexedit,hostid,hostname,httpd,hush,hwclock,i2cdetect,i2cdump,i2cget,i2cset,i2ctransfer,id,ifconfig,ifdown,ifenslave,ifplugd,ifup,inetd,init,insmod,install,ionice,iostat,ip,ipaddr,ipcalc,ipcrm,ipcs,iplink,ipneigh,iproute,iprule,iptunnel,kbd_mode,kill,killall,killall5,klogd,last,less,link,linux32,linux64,linuxrc,ln,loadfont,loadkmap,logger,login,logname,logread,losetup,lpd,lpq,lpr,ls,lsattr,lsmod,lsof,lspci,lsscsi,lsusb,lzcat,lzma,lzop,makedevs,makemime,man,md5sum,mdev,mesg,microcom,mim,mkdir,mkdosfs,mke2fs,mkfifo,mkfs_ext2,mkfs_minix,mkfs_vfat,mknod,mkpasswd,mkswap,mktemp,modinfo,modprobe,more,mount,mountpoint,mpstat,mt,mv,nameif,nanddump,nandwrite,nbdclient,nc,netstat,nice,nl,nmeter,nohup,nologin,nproc,nsenter,nslookup,ntpd,od,openvt,partprobe,passwd,paste,patch,pgrep,pidof,ping,ping6,pipe_progress,pivot_root,pkill,pmap,popmaildir,poweroff,powertop,printenv,printf,ps,pscan,pstree,pwd,pwdx,raidautorun,rdate,rdev,readahead,readlink,readprofile,realpath,reboot,reformime,remove_shell,renice,reset,resize,resume,rev,rm,rmdir,rmmod,route,rpm,rpm2cpio,rtcwake,run_init,run_parts,runlevel,runsv,runsvdir,rx,script,scriptreplay,sed,seedrng,sendmail,seq,setarch,setconsole,setfattr,setfont,setkeycodes,setlogcons,setpriv,setserial,setsid,setuidgid,sh,sh_is_ash,sha1sum,sha256sum,sha3sum,sha512sum,showkey,shred,shuf,slattach,sleep,smemcap,softlimit,sort,split,ssl_client,start_stop_daemon,stat,strings,stty,su,sulogin,sum,sv,svc,svlogd,svok,swapoff,swapon,switch_root,sync,sysctl,syslogd,tac,tail,tar,taskset,tc,tcpsvd,tee,telnet,telnetd,test,test1,test2,tftp,tftpd,time,timeout,top,touch,tr,traceroute,traceroute6,tree,true,truncate,ts,tsort,tty,ttysize,tunctl,ubiattach,ubidetach,ubimkvol,ubirename,ubirmvol,ubirsvol,ubiupdatevol,udhcpc,udhcpc6,udhcpd,udpsvd,uevent,umount,uname,unexpand,uniq,unix2dos,unlink,unlzma,unshare,unxz,unzip,uptime,users,usleep,uudecode,uuencode,vconfig,vi,vlock,volname,w,wall,watch,watchdog,wc,which,who,whoami,whois,xargs,xxd,xz,xzcat,yes,zcat,zcip" 132 | }, 133 | { 134 | "name": "curl", 135 | "description": "A command line tool for transferring files with URL syntax", 136 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/curl", 137 | "size": "5.3 MB", 138 | "b3sum": "7b691bbdce2dca992c02bacd100a10e1a0d7ea7d535f377f6d32b99d13560ac9", 139 | "sha256": "58274d111e109a01c6a4a43d318561ce119a128b8e5d7cbcc17fc85535a28c8e", 140 | "build_date": "2024-08-20T19:10:00", 141 | "repo_url": "https://github.com/curl/curl", 142 | "repo_author": "curl", 143 | "repo_info": "A command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, RTMP, RTMPS, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features", 144 | "repo_updated": "2024-08-21T22:01:56Z", 145 | "repo_released": "2024-07-31T07:08:30Z", 146 | "repo_version": "curl-8_9_1", 147 | "repo_stars": "35191", 148 | "repo_language": "C", 149 | "repo_license": "Other", 150 | "repo_topics": "c, client, curl, ftp, gopher, hacktoberfest, http, https, imaps, ldap, libcurl, library, mqtt, pop3, scp, sftp, transfer-data, transferring-data, user-agent, websocket", 151 | "web_url": "https://github.com/curl/curl", 152 | "extra_bins": "curl_cacert.pem" 153 | }, 154 | { 155 | "name": "curl_cacert.pem", 156 | "description": "A command line tool for transferring files with URL syntax", 157 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/curl_cacert.pem", 158 | "size": "228.63 kB", 159 | "b3sum": "d0993af134271f1511e1b5f01a2bfe216d4bf22d8c5d0f9cd60f9f6b9626d65e", 160 | "sha256": "1bf458412568e134a4514f5e170a328d11091e071c7110955c9884ed87972ac9", 161 | "build_date": "2024-07-23T10:43:05", 162 | "repo_url": "https://github.com/curl/curl", 163 | "repo_author": "curl", 164 | "repo_info": "A command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, RTMP, RTMPS, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features", 165 | "repo_updated": "2024-08-21T22:01:56Z", 166 | "repo_released": "2024-07-31T07:08:30Z", 167 | "repo_version": "curl-8_9_1", 168 | "repo_stars": "35191", 169 | "repo_language": "C", 170 | "repo_license": "Other", 171 | "repo_topics": "c, client, curl, ftp, gopher, hacktoberfest, http, https, imaps, ldap, libcurl, library, mqtt, pop3, scp, sftp, transfer-data, transferring-data, user-agent, websocket", 172 | "web_url": "https://github.com/curl/curl", 173 | "extra_bins": "curl" 174 | }, 175 | { 176 | "name": "jless", 177 | "description": "command-line JSON viewer designed for reading, exploring, and searching through JSON data.", 178 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/jless", 179 | "size": "2.02 MB", 180 | "b3sum": "24c1dbd5b0e09e8bd9e19f9c523eb80f4522194da65c8096e4156ffa3061b360", 181 | "sha256": "5599bd82880575ffefcbf44bbcfa78f356f0e580a30ddfef329597d59cede0d3", 182 | "build_date": "2024-07-23T10:44:39", 183 | "repo_url": "https://github.com/PaulJuliusMartinez/jless", 184 | "repo_author": "PaulJuliusMartinez", 185 | "repo_info": "jless is a command-line JSON viewer designed for reading, exploring, and searching through JSON data.", 186 | "repo_updated": "2024-06-01T20:34:10Z", 187 | "repo_released": "2023-07-17T02:51:34Z", 188 | "repo_version": "v0.9.0", 189 | "repo_stars": "4672", 190 | "repo_language": "Rust", 191 | "repo_license": "MIT License", 192 | "repo_topics": "cli, json, rust", 193 | "web_url": "https://github.com/PaulJuliusMartinez/jless" 194 | }, 195 | { 196 | "name": "pingmole", 197 | "description": "CLI that helps to filter pingmole servers and pick the closest one.", 198 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/pingmole", 199 | "size": "6.93 MB", 200 | "b3sum": "69dbf1899dfc2ff7b4c51b9733d7237660ad58cc538c177c6c491592dbfc0a4f", 201 | "sha256": "c0b3c04acfadd816806cb860bf9391b07f4b96b26321264da57865dbd2dfdca5", 202 | "build_date": "2024-08-20T19:17:21", 203 | "repo_url": "https://github.com/norskeld/pingmole", 204 | "repo_author": "norskeld", 205 | "repo_info": "CLI that helps to filter Mullvad servers and pick the closest one.", 206 | "repo_updated": "2024-04-16T11:28:34Z", 207 | "repo_stars": "4", 208 | "repo_language": "Rust", 209 | "repo_license": "MIT License", 210 | "repo_topics": "cli, mullvad, rust", 211 | "web_url": "https://github.com/norskeld/pingmole" 212 | }, 213 | { 214 | "name": "pspy", 215 | "description": "Monitor linux processes without root permissions", 216 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/pspy", 217 | "size": "4.02 MB", 218 | "b3sum": "4ec4d98aed92170cc85486e8a1f0d0994db9ebae38256924d466f7c34ab01b48", 219 | "sha256": "fe13aae109aaaa2a70d3cfa3f1bcf139deffebeb233e151db38acbd2dec0863c", 220 | "build_date": "2024-08-13T19:19:52", 221 | "repo_url": "https://github.com/DominicBreuker/pspy", 222 | "repo_author": "DominicBreuker", 223 | "repo_info": "Monitor linux processes without root permissions", 224 | "repo_updated": "2023-01-17T21:09:22Z", 225 | "repo_released": "2023-01-17T21:10:08Z", 226 | "repo_version": "v1.2.1", 227 | "repo_stars": "4817", 228 | "repo_language": "Go", 229 | "repo_license": "GNU General Public License v3.0", 230 | "repo_topics": "ctf, enumeration, golang, pentesting, privesc, security", 231 | "web_url": "https://github.com/DominicBreuker/pspy" 232 | }, 233 | { 234 | "name": "rclone", 235 | "description": "Rsync for cloud storage", 236 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/rclone", 237 | "size": "85.15 MB", 238 | "b3sum": "7f3cc3d51d838746e6c38a704a74ea880d1b8ed7b053287da4c33384e786fd1c", 239 | "sha256": "d1bee348b8c6be2aa51bec49ccec301f713d76d8ca0dd3c7815cafc4cd72c673", 240 | "build_date": "2024-08-20T19:19:47", 241 | "repo_url": "https://github.com/rclone/rclone", 242 | "repo_author": "rclone", 243 | "repo_info": "rsync for cloud storage - Google Drive, S3, Dropbox, Backblaze B2, One Drive, Swift, Hubic, Wasabi, Google Cloud Storage, Azure Blob, Azure Files, Yandex Files", 244 | "repo_updated": "2024-08-21T06:55:48Z", 245 | "repo_released": "2024-06-14T16:58:29Z", 246 | "repo_version": "v1.67.0", 247 | "repo_stars": "45722", 248 | "repo_language": "Go", 249 | "repo_license": "MIT License", 250 | "repo_topics": "azure-blob, azure-blob-storage, azure-files, backblaze-b2, cloud-storage, dropbox, encryption, ftp, fuse-filesystem, go, golang, google-cloud-storage, google-drive, onedrive, openstack-swift, rclone, s3, sftp, sync, webdav", 251 | "web_url": "https://github.com/rclone/rclone" 252 | }, 253 | { 254 | "name": "rsync", 255 | "description": "An open source utility that provides fast incremental file transfer.", 256 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/rsync", 257 | "size": "5.26 MB", 258 | "b3sum": "13aea0ce350d84537ff98efa2c102c0f416e4d2a26e81251ecd040fdadbbbd95", 259 | "sha256": "a0801fd18f4a11001df422c39a1a4f88902b6c277344ed3c0acff2d8066fc189", 260 | "build_date": "2024-08-20T19:20:22", 261 | "repo_url": "https://github.com/WayneD/rsync", 262 | "repo_author": "RsyncProject", 263 | "repo_info": "An open source utility that provides fast incremental file transfer. It also has useful features for backup and restore operations among many other use cases.", 264 | "repo_updated": "2024-07-06T04:30:43Z", 265 | "repo_version": "v3.3.0", 266 | "repo_stars": "2612", 267 | "repo_language": "C", 268 | "repo_license": "Other", 269 | "web_url": "https://github.com/WayneD/rsync", 270 | "extra_bins": "rsync-ssl" 271 | }, 272 | { 273 | "name": "sudo", 274 | "description": "Download with resuming and segmented downloading", 275 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/sudo", 276 | "size": "250.38 kB", 277 | "b3sum": "261a7ec6cf5ed2fbc82f8128f2583eda7faeb8939b9e08143046f0b046e504ae", 278 | "sha256": "9e56787b3ca489a9eb9e3a64f54944aa92c728d18576972ef7ef6bb10ca6462c", 279 | "build_date": "2024-07-23T10:56:44", 280 | "repo_url": "https://github.com/agnostic-apollo/sudo", 281 | "repo_author": "agnostic-apollo", 282 | "repo_info": "A wrapper script to drop to the supported shells or execute shell script files or their text passed as an argument with superuser (root) context in termux", 283 | "repo_updated": "2021-04-10T21:03:10Z", 284 | "repo_released": "2021-04-10T21:03:11Z", 285 | "repo_version": "v0.2.0", 286 | "repo_stars": "86", 287 | "repo_language": "Shell", 288 | "repo_license": "MIT License", 289 | "repo_topics": "bash, tasker, termux, termux-tasker", 290 | "web_url": "https://github.com/agnostic-apollo/sudo" 291 | }, 292 | { 293 | "name": "tailscale", 294 | "description": "The easiest, most secure way to use WireGuard and 2FA", 295 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/tailscale", 296 | "size": "12.01 MB", 297 | "b3sum": "b15dc153a46a3eab437bb0e2ce4e89f4cc41fb0c052adbc9944e75507f15d12a", 298 | "sha256": "6682169b0c93a2195a5c5a161db0c180b442e53fc51e008abb7bb633cb27cdcd", 299 | "build_date": "2024-08-20T19:26:17", 300 | "repo_url": "https://github.com/tailscale/tailscale", 301 | "repo_author": "tailscale", 302 | "repo_info": "The easiest, most secure way to use WireGuard and 2FA.", 303 | "repo_updated": "2024-08-21T21:56:51Z", 304 | "repo_released": "2024-08-19T17:47:13Z", 305 | "repo_version": "v1.72.0", 306 | "repo_stars": "18250", 307 | "repo_language": "Go", 308 | "repo_license": "BSD 3-Clause New or Revised License", 309 | "repo_topics": "2fa, oauth, sso, tailscale, vpn, wireguard", 310 | "web_url": "https://github.com/tailscale/tailscale", 311 | "extra_bins": "tailscale_combined,tailscaled" 312 | }, 313 | { 314 | "name": "tailscale_combined", 315 | "description": "The easiest, most secure way to use WireGuard and 2FA", 316 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/tailscale_combined", 317 | "size": "26.25 MB", 318 | "b3sum": "6950ca1eb2c82780331757a543c5ea6851bb2b6af103a5af89d2bb6e54779695", 319 | "sha256": "8f50e7a1b75cd9a9b4e8cb30987c817b31571719efe0098753e1fe1ebeac4f35", 320 | "build_date": "2024-08-20T19:26:17", 321 | "repo_url": "https://github.com/tailscale/tailscale", 322 | "repo_author": "tailscale", 323 | "repo_info": "The easiest, most secure way to use WireGuard and 2FA.", 324 | "repo_updated": "2024-08-21T21:56:51Z", 325 | "repo_released": "2024-08-19T17:47:13Z", 326 | "repo_version": "v1.72.0", 327 | "repo_stars": "18250", 328 | "repo_language": "Go", 329 | "repo_license": "BSD 3-Clause New or Revised License", 330 | "repo_topics": "2fa, oauth, sso, tailscale, vpn, wireguard", 331 | "web_url": "https://github.com/tailscale/tailscale", 332 | "extra_bins": "tailscale,tailscaled" 333 | }, 334 | { 335 | "name": "tailscaled", 336 | "description": "The easiest, most secure way to use WireGuard and 2FA", 337 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/tailscaled", 338 | "size": "24.14 MB", 339 | "b3sum": "2a105a9f8f3995d04f5af6e52cb7c566c5441fefda6766b27b00c630145d6794", 340 | "sha256": "b64f9f1c5b3cfbafe778b407c5e0465686df615dd65bc39b7c11c13ebb7bece7", 341 | "build_date": "2024-08-20T19:26:17", 342 | "repo_url": "https://github.com/tailscale/tailscale", 343 | "repo_author": "tailscale", 344 | "repo_info": "The easiest, most secure way to use WireGuard and 2FA.", 345 | "repo_updated": "2024-08-21T21:56:51Z", 346 | "repo_released": "2024-08-19T17:47:13Z", 347 | "repo_version": "v1.72.0", 348 | "repo_stars": "18250", 349 | "repo_language": "Go", 350 | "repo_license": "BSD 3-Clause New or Revised License", 351 | "repo_topics": "2fa, oauth, sso, tailscale, vpn, wireguard", 352 | "web_url": "https://github.com/tailscale/tailscale", 353 | "extra_bins": "tailscale,tailscale_combined" 354 | }, 355 | { 356 | "name": "wget", 357 | "description": "Tool for retrieving files using HTTP, HTTPS, and FTP", 358 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/wget", 359 | "size": "5.81 MB", 360 | "b3sum": "5746d29935890e8624473a66e28cd28576f975d3a7c46ac7cdbe150798b22bbd", 361 | "sha256": "2712d5437267cee5ff3df91e6bbfa3e9347625bd217315beac660bdfb63ceb26", 362 | "build_date": "2024-08-20T19:29:04", 363 | "web_url": "https://www.gnu.org/software/wget/", 364 | "extra_bins": "wget_cacert.pem" 365 | }, 366 | { 367 | "name": "wget_cacert.pem", 368 | "description": "Tool for retrieving files using HTTP, HTTPS, and FTP", 369 | "download_url": "https://bin.ajam.dev/arm64_v8a_Android/wget_cacert.pem", 370 | "size": "228.63 kB", 371 | "b3sum": "d0993af134271f1511e1b5f01a2bfe216d4bf22d8c5d0f9cd60f9f6b9626d65e", 372 | "sha256": "1bf458412568e134a4514f5e170a328d11091e071c7110955c9884ed87972ac9", 373 | "build_date": "2024-07-23T11:00:26", 374 | "web_url": "https://www.gnu.org/software/wget/", 375 | "extra_bins": "wget" 376 | } 377 | ] -------------------------------------------------------------------------------- /misc/cmd/modMetadata/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/xplshn/bigdl/misc/cmd/modMetadata 2 | 3 | go 1.22.0 4 | -------------------------------------------------------------------------------- /misc/cmd/modMetadata/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/url" 9 | "strings" 10 | ) 11 | 12 | type labeledString struct { 13 | string string 14 | label string 15 | } 16 | 17 | type Item struct { 18 | Name string `json:"name"` 19 | // RealName *string `json:"real_name,omitempty"` 20 | Description string `json:"description,omitempty"` 21 | DownloadURL string `json:"download_url,omitempty"` 22 | Size string `json:"size,omitempty"` 23 | B3sum string `json:"b3sum,omitempty"` 24 | Sha256 string `json:"sha256,omitempty"` 25 | BuildDate string `json:"build_date,omitempty"` 26 | RepoURL string `json:"repo_url,omitempty"` 27 | RepoAuthor string `json:"repo_author,omitempty"` 28 | RepoInfo string `json:"repo_info,omitempty"` 29 | RepoUpdated string `json:"repo_updated,omitempty"` 30 | RepoReleased string `json:"repo_released,omitempty"` 31 | RepoVersion string `json:"repo_version,omitempty"` 32 | RepoStars string `json:"repo_stars,omitempty"` 33 | RepoLanguage string `json:"repo_language,omitempty"` 34 | RepoLicense string `json:"repo_license,omitempty"` 35 | RepoTopics string `json:"repo_topics,omitempty"` 36 | WebURL string `json:"web_url,omitempty"` 37 | ExtraBins string `json:"extra_bins,omitempty"` 38 | } 39 | 40 | func urldecode(encoded string) (string, error) { 41 | return url.PathUnescape(encoded) 42 | } 43 | 44 | func processItems(items []Item, arch string, repo_label string) []Item { 45 | for i, item := range items { 46 | // Parse the download URL to get its path 47 | parsedURL, err := url.Parse(item.DownloadURL) 48 | if err != nil { 49 | // Handle the error appropriately 50 | continue 51 | } 52 | 53 | // Extract the path from the URL and remove leading slashes 54 | cleanPath := parsedURL.Path 55 | if strings.HasPrefix(cleanPath, "/") { 56 | cleanPath = cleanPath[1:] 57 | } 58 | 59 | // Remove the architecture-specific path from the download URL path 60 | if strings.HasPrefix(cleanPath, arch+"/") { 61 | cleanPath = strings.TrimPrefix(cleanPath, arch+"/") 62 | } 63 | 64 | // Remove the repo's label 65 | if strings.HasPrefix(cleanPath, repo_label+"/") { 66 | cleanPath = strings.TrimPrefix(cleanPath, repo_label+"/") 67 | } 68 | 69 | // Ensure real_name is always set 70 | items[i].Name = cleanPath 71 | } 72 | return items 73 | } 74 | 75 | func downloadJSON(url string) ([]Item, error) { 76 | resp, err := http.Get(url) 77 | if err != nil { 78 | return nil, err 79 | } 80 | defer resp.Body.Close() 81 | 82 | body, err := ioutil.ReadAll(resp.Body) 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | var items []Item 88 | err = json.Unmarshal(body, &items) 89 | if err != nil { 90 | return nil, err 91 | } 92 | 93 | return items, nil 94 | } 95 | 96 | func saveJSON(filename string, items []Item) error { 97 | jsonData, err := json.MarshalIndent(items, "", " ") 98 | if err != nil { 99 | return err 100 | } 101 | 102 | err = ioutil.WriteFile(filename, jsonData, 0644) 103 | if err != nil { 104 | return err 105 | } 106 | 107 | return nil 108 | } 109 | 110 | func main() { 111 | validatedArchs := []string{"x86_64_Linux", "aarch64_arm64_Linux", "arm64_v8a_Android"} 112 | 113 | for _, arch := range validatedArchs { 114 | repos := []labeledString{ 115 | /* ,---- [ NOTICE ] 116 | |This "Toolpacks" label won't resolve to true against "Remove the repo's label" section 117 | | in processItems() Its okay because Toolpacks is the main repo. - 118 | | Baseutils needs a Label there because Baseutils is INSIDE of "Toolpacks" 119 | `---- 120 | /*/ 121 | {"https://bin.ajam.dev/" + arch + "/METADATA.json", "Toolpacks"}, 122 | {"https://bin.ajam.dev/" + arch + "/Baseutils/METADATA.json", "Baseutils"}, 123 | } 124 | 125 | for _, repo := range repos { 126 | items, err := downloadJSON(repo.string) 127 | if err != nil { 128 | fmt.Printf("Error downloading JSON from %s: %v\n", repo, err) 129 | continue 130 | } 131 | 132 | processedItems := processItems(items, arch, repo.label) 133 | 134 | outputFile := fmt.Sprintf("%s.bigdl_%s.json", repo.label, arch) 135 | 136 | if err := saveJSON(outputFile, processedItems); err != nil { 137 | fmt.Printf("Error saving JSON to %s: %v\n", outputFile, err) 138 | continue 139 | } 140 | fmt.Printf("Processed and saved to %s\n", outputFile) 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /remove.go: -------------------------------------------------------------------------------- 1 | // remove.go // This file implements the functionality of "remove" //> 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | func remove(binariesToRemove []string) { 11 | for _, binaryName := range binariesToRemove { 12 | // Use the base name of binaryName for constructing the cachedFile path 13 | baseName := filepath.Base(binaryName) 14 | installPath := filepath.Join(InstallDir, baseName) 15 | err := os.Remove(installPath) 16 | if err != nil { 17 | if os.IsNotExist(err) { 18 | fmt.Fprintf(os.Stderr, "Warning: '%s' does not exist in %s\n", baseName, InstallDir) 19 | } else { 20 | fmt.Fprintf(os.Stderr, "Error: Failed to remove '%s' from %s. %v\n", baseName, InstallDir, err) 21 | } 22 | continue 23 | } 24 | fmt.Printf("'%s' removed from %s\n", baseName, InstallDir) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /run.go: -------------------------------------------------------------------------------- 1 | // run.go // This file implements the "run" functionality //> 2 | package main 3 | 4 | import ( 5 | "flag" 6 | "fmt" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | "sort" 11 | "strings" 12 | "syscall" 13 | "time" 14 | ) 15 | 16 | var ( 17 | verboseMode bool 18 | silentMode bool 19 | ) 20 | 21 | // ReturnCachedFile retrieves the cached file location. Returns an empty string and error code 1 if not found. 22 | func ReturnCachedFile(binaryName string) (string, int) { 23 | cachedBinary := filepath.Join(TEMPDIR, binaryName) 24 | if fileExists(cachedBinary) { 25 | return cachedBinary, 0 26 | } 27 | // The file does not exist, return an error 28 | return "", 1 29 | } 30 | 31 | // RunFromCache runs the binary from cache or fetches it if not found. 32 | func RunFromCache(binaryName string, args []string) { 33 | // purifyVars is a function to purify binaryName and args. 34 | purifyVars := func() { 35 | if len(args) > 0 { 36 | binaryName = (args)[0] // Purify binaryName 37 | args = (args)[1:] // Appropriately set args to exclude any of the flags 38 | } else { 39 | errorOut("Error: Binary name not provided after flag.\n") 40 | } 41 | } 42 | 43 | // Process flags 44 | verbose := flag.Bool("verbose", false, "Enable verbose mode") 45 | silent := flag.Bool("silent", false, "Enable silent mode") 46 | transparent := flag.Bool("transparent", false, "Enable transparent mode") 47 | 48 | flagsAndBinaryName := append(strings.Fields(binaryName), args...) 49 | flag.CommandLine.Parse(flagsAndBinaryName) 50 | 51 | if *verbose && *silent { 52 | errorOut("error: --verbose and --silent are mutually exclusive\n") 53 | } 54 | 55 | if *verbose { 56 | verboseMode = true 57 | purifyVars() 58 | } 59 | 60 | if *silent { 61 | silentMode = true 62 | UseProgressBar = false 63 | purifyVars() 64 | } 65 | 66 | if *transparent { 67 | purifyVars() 68 | binaryPath, _ := exec.LookPath(binaryName) // is it okay to ignore the err channel of LookPath? 69 | 70 | if binaryPath != "" { 71 | if !silentMode { 72 | fmt.Printf("Running '%s' from PATH...\n", binaryName) 73 | } 74 | runBinary(binaryPath, args, verboseMode) 75 | } 76 | } 77 | 78 | if binaryName == "" { 79 | errorOut("error: Binary name not provided\n") 80 | } 81 | 82 | // Use the base name of binaryName to construc the cachedFile path. This way requests like toybox/wget are supported 83 | cachedFile := filepath.Join(TEMPDIR, filepath.Base(binaryName)) 84 | 85 | if fileExists(cachedFile) && isExecutable(cachedFile) { 86 | if !silentMode { 87 | fmt.Printf("Running '%s' from cache...\n", binaryName) 88 | } 89 | runBinary(cachedFile, args, verboseMode) 90 | cleanCache() 91 | } else { 92 | if verboseMode { 93 | fmt.Printf("Couldn't find '%s' in the cache. Fetching a new one...\n", binaryName) 94 | } 95 | InstallDir = TEMPDIR 96 | InstallMessage = "" 97 | if err := installCommand(silentMode, binaryName); err != nil { 98 | errorOut("%v\n", err) 99 | } 100 | cleanCache() 101 | runBinary(cachedFile, args, verboseMode) 102 | } 103 | } 104 | 105 | // runBinary executes the binary with the given arguments. 106 | func runBinary(binaryPath string, args []string, verboseMode bool) { 107 | // Set the Controls for the Heart of the Sun 108 | cmd := exec.Command(binaryPath, args...) 109 | cmd.Stdout = os.Stdout 110 | cmd.Stderr = os.Stderr 111 | cmd.Stdin = os.Stdin 112 | 113 | err := cmd.Run() 114 | exitCode := cmd.ProcessState.ExitCode() 115 | 116 | if err != nil && verboseMode { 117 | fmt.Printf("The program (%s) errored out with a non-zero exit code (%d).\n", binaryPath, exitCode) 118 | } 119 | 120 | os.Exit(exitCode) 121 | } 122 | 123 | // cleanCache removes the oldest binaries when the cache size exceeds MaxCacheSize. 124 | func cleanCache() { 125 | // Get a list of all binaries in the cache directory 126 | files, err := os.ReadDir(TEMPDIR) 127 | if err != nil { 128 | fmt.Printf("Error reading cache directory: %v\n", err) 129 | return 130 | } 131 | 132 | // Check if the cache size has exceeded the limit 133 | if len(files) <= MaxCacheSize { 134 | return 135 | } 136 | 137 | // Use a custom struct to hold file info and atime 138 | type fileWithAtime struct { 139 | info os.FileInfo 140 | atime time.Time 141 | } 142 | 143 | // Convert os.DirEntry to fileWithAtime and track the last accessed time 144 | var filesWithAtime []fileWithAtime 145 | for _, entry := range files { 146 | fileInfo, err := entry.Info() 147 | if err != nil { 148 | fmt.Printf("Error getting file info: %v\n", err) 149 | continue 150 | } 151 | 152 | // Use syscall to get atime 153 | var stat syscall.Stat_t 154 | err = syscall.Stat(filepath.Join(TEMPDIR, entry.Name()), &stat) 155 | if err != nil { 156 | fmt.Printf("Error getting file stat: %v\n", err) 157 | continue 158 | } 159 | 160 | atime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) 161 | filesWithAtime = append(filesWithAtime, fileWithAtime{info: fileInfo, atime: atime}) 162 | } 163 | 164 | // Sort files by last accessed time 165 | sort.Slice(filesWithAtime, func(i, j int) bool { 166 | return filesWithAtime[i].atime.Before(filesWithAtime[j].atime) 167 | }) 168 | 169 | // Delete the oldest binaries 170 | for i := 0; i < BinariesToDelete; i++ { 171 | err := os.Remove(filepath.Join(TEMPDIR, filesWithAtime[i].info.Name())) 172 | if err != nil { 173 | if !silentMode { // Check if not in silent mode before printing 174 | fmt.Printf("Error removing file: %v\n", err) 175 | } 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /stubdl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DEST="/tmp/._bdlstub_bigdl.bin" 4 | 5 | # Determine architecture 6 | ARCH="$(uname -m)" 7 | case "$ARCH" in 8 | x86_64) ARCH_SUFFIX="amd64" ;; 9 | aarch64) ARCH_SUFFIX="arm64" ;; 10 | *) echo "Unsupported architecture: $ARCH"; exit 1 ;; 11 | esac 12 | 13 | BIGDL_URL="https://github.com/xplshn/bigdl/releases/latest/download/bigdl_${ARCH_SUFFIX}_upx" 14 | 15 | # Handle --install option 16 | if [ "$1" = "--install" ]; then 17 | DEST="$2" 18 | shift 2 19 | fi 20 | 21 | # Function to download the binary 22 | download_bigdl() { 23 | if command -v wget >/dev/null 2>&1; then 24 | wget -q "$BIGDL_URL" -O "$DEST" 25 | elif command -v curl >/dev/null 2>&1; then 26 | curl -qfsSL "$BIGDL_URL" -o "$DEST" 27 | else 28 | echo "Neither wget nor curl is available." 29 | exit 1 30 | fi 31 | } 32 | 33 | # Check if binary exists and is executable 34 | if [ -e "$DEST" ] && [ ! "$1" = "--install" ]; then 35 | # Run the binary 36 | "$DEST" "$@" 37 | else 38 | # Download and install the binary 39 | mkdir -p "$(dirname "$DEST")" 40 | download_bigdl 41 | 42 | if [ "$1" = "--install" ]; then 43 | chmod +x "$DEST" 44 | echo "BIGDL IS NOW AVAILABLE. ($DEST)" 45 | exit 0 46 | fi 47 | 48 | # Make the binary executable and run it 49 | chmod +x "$DEST" 50 | "$DEST" "$@" 51 | fi 52 | -------------------------------------------------------------------------------- /update.go: -------------------------------------------------------------------------------- 1 | // update.go // This file holds the implementation for the "update" functionality - (parallel) //> 2 | package main 3 | 4 | import ( 5 | "crypto/sha256" 6 | "encoding/hex" 7 | "fmt" 8 | "io" 9 | "os" 10 | "path/filepath" 11 | "sync" 12 | "sync/atomic" 13 | ) 14 | 15 | // update checks for updates to the valid programs and installs any that have changed. 16 | func update(programsToUpdate []string) error { 17 | // 'Configure' external functions 18 | UseProgressBar = false 19 | InstallUseCache = false 20 | 21 | // Initialize counters 22 | var ( 23 | skipped, updated, errors, toBeChecked uint32 24 | checked uint32 25 | errorMessages string 26 | padding = " " 27 | ) 28 | 29 | // Call validateProgramsFrom with InstallDir and programsToUpdate 30 | programsToUpdate, err := validateProgramsFrom(InstallDir, programsToUpdate) 31 | if err != nil { 32 | fmt.Println("Error validating programs:", err) 33 | return err 34 | } 35 | 36 | // Calculate toBeChecked 37 | toBeChecked = uint32(len(programsToUpdate)) 38 | 39 | // Use a mutex for thread-safe updates to the progress 40 | var progressMutex sync.Mutex 41 | 42 | // Use a wait group to wait for all programs to finish updating 43 | var wg sync.WaitGroup 44 | 45 | // Iterate over programsToUpdate and download/update each one concurrently 46 | for _, program := range programsToUpdate { 47 | // Increment the WaitGroup counter 48 | wg.Add(1) 49 | 50 | // Launch a goroutine to update the program 51 | go func(program string) { 52 | defer wg.Done() 53 | 54 | installPath := filepath.Join(InstallDir, program) 55 | if !fileExists(installPath) { 56 | progressMutex.Lock() 57 | atomic.AddUint32(&checked, 1) 58 | atomic.AddUint32(&skipped, 1) 59 | truncatePrintf("\033[2K\r<%d/%d> %s | Warning: Tried to update a non-existent program %s. Skipping.", atomic.LoadUint32(&checked), toBeChecked, padding, program) 60 | progressMutex.Unlock() 61 | return 62 | } 63 | localSHA256, err := getLocalSHA256(installPath) 64 | if err != nil { 65 | progressMutex.Lock() 66 | atomic.AddUint32(&checked, 1) 67 | atomic.AddUint32(&skipped, 1) 68 | truncatePrintf("\033[2K\r<%d/%d> %s | Warning: Failed to get SHA256 for %s. Skipping.", atomic.LoadUint32(&checked), toBeChecked, padding, program) 69 | progressMutex.Unlock() 70 | return 71 | } 72 | 73 | binaryInfo, err := getBinaryInfo(program) 74 | if err != nil { 75 | progressMutex.Lock() 76 | atomic.AddUint32(&checked, 1) 77 | atomic.AddUint32(&skipped, 1) 78 | truncatePrintf("\033[2K\r<%d/%d> %s | Warning: Failed to get metadata for %s. Skipping.", atomic.LoadUint32(&checked), toBeChecked, padding, program) 79 | progressMutex.Unlock() 80 | return 81 | } 82 | 83 | // Skip if the SHA field is null 84 | if binaryInfo.SHA256 == "" { 85 | progressMutex.Lock() 86 | atomic.AddUint32(&checked, 1) 87 | atomic.AddUint32(&skipped, 1) 88 | truncatePrintf("\033[2K\r<%d/%d> %s | Skipping %s because the SHA256 field is null.", atomic.LoadUint32(&checked), toBeChecked, padding, program) 89 | progressMutex.Unlock() 90 | return 91 | } 92 | 93 | // Start update process 94 | truncatePrintf("\033[2K\r<%d/%d> %s | Looking for differences in %s against the repo's...", atomic.LoadUint32(&checked), toBeChecked, padding, program) 95 | if checkDifferences(localSHA256, binaryInfo.SHA256) == 1 { 96 | truncatePrintf("\033[2K\r<%d/%d> %s | The repo's version of %s differs from yours. Updating...", atomic.LoadUint32(&checked), toBeChecked, padding, program) 97 | err := installCommand(true, program) 98 | if err != nil { 99 | progressMutex.Lock() 100 | atomic.AddUint32(&errors, 1) 101 | errorMessages += sanitizeString(fmt.Sprintf("Failed to update '%s', please check this file's properties, etc\n", program)) 102 | progressMutex.Unlock() 103 | return 104 | } 105 | progressMutex.Lock() 106 | atomic.AddUint32(&checked, 1) 107 | atomic.AddUint32(&updated, 1) 108 | truncatePrintf("\033[2K\r<%d/%d> %s | Successfully updated %s.", atomic.LoadUint32(&checked), toBeChecked, padding, program) 109 | progressMutex.Unlock() 110 | } else { 111 | progressMutex.Lock() 112 | atomic.AddUint32(&checked, 1) 113 | truncatePrintf("\033[2K\r<%d/%d> %s | No updates available for %s.", atomic.LoadUint32(&checked), toBeChecked, padding, program) 114 | progressMutex.Unlock() 115 | } 116 | }(program) 117 | } 118 | 119 | // Wait for all goroutines to finish 120 | wg.Wait() 121 | 122 | // Prepare final counts 123 | finalCounts := fmt.Sprintf("\033[2K\rSkipped: %d\tUpdated: %d\tChecked: %d", atomic.LoadUint32(&skipped), atomic.LoadUint32(&updated), uint32(int(atomic.LoadUint32(&checked)))) 124 | if errors > 0 { 125 | finalCounts += fmt.Sprintf("\tErrors: %d", atomic.LoadUint32(&errors)) 126 | } 127 | // Print final counts 128 | fmt.Println(finalCounts) 129 | for error := range errorMessages { 130 | fmt.Println(error) 131 | } 132 | 133 | return nil 134 | } 135 | 136 | // getLocalSHA256 calculates the SHA256 checksum of the local file. 137 | func getLocalSHA256(filePath string) (string, error) { 138 | // Open the file for reading 139 | file, err := os.Open(filePath) 140 | if err != nil { 141 | return "", fmt.Errorf("failed to open file: %v", err) 142 | } 143 | defer file.Close() 144 | 145 | // Calculate SHA256 checksum 146 | hasher := sha256.New() 147 | if _, err := io.Copy(hasher, file); err != nil { 148 | return "", fmt.Errorf("failed to calculate SHA256: %v", err) 149 | } 150 | sha256Checksum := hex.EncodeToString(hasher.Sum(nil)) 151 | 152 | return sha256Checksum, nil 153 | } 154 | 155 | func checkDifferences(localSHA256, remoteSHA256 string) int { 156 | if localSHA256 != remoteSHA256 { 157 | return 1 158 | } 159 | return 0 160 | } 161 | --------------------------------------------------------------------------------