diff --git a/.gitignore b/.gitignore index dbe9c82..af67c37 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.vscode/ \ No newline at end of file +.vscode/ +KFPM/KFPM diff --git a/KFPM/main.go b/KFPM/main.go index 5a12f95..7b51840 100644 --- a/KFPM/main.go +++ b/KFPM/main.go @@ -8,101 +8,110 @@ package main import ( - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "os" - "os/exec" - "slices" - "strings" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "slices" + "strings" ) const ( - registryURL = "https://kf.penguins184.xyz/registry.json" - registryBase = "https://kf.penguins184.xyz/" - installedFile = "/mnt/us/.KFPM/installed.txt" + installedFile = "/mnt/us/.KFPM/installed.txt" + registryFile = "/mnt/us/.KFPM/repositories.txt" ) var ( - registry = fetchRegistry() - installed = getInstalled() - ABI = fetchABI() + registry []Package + installed []string + ABI string ) type Package struct { - Name string `json:"name"` - Uri string `json:"uri"` - Dependencies []string `json:"dependencies"` - Description string `json:"description"` - Author string `json:"author"` - ABI []string `json:"ABI"` + Name string `json:"name"` + Uri string `json:"uri"` + Dependencies []string `json:"dependencies"` + Description string `json:"description"` + Author string `json:"author"` + ABI []string `json:"ABI"` + Repo string `json:"-"` } func main() { - ensureInstalledDir() - - args := os.Args[1:] - - if len(args) == 0 { - help() - return - } - - verbose := len(args) > 2 && args[2] == "-v" - - switch args[0] { - case "-i": - if len(args) < 2 { - fmt.Println("Oops! -i Requires A Package Name!") - return - } - pkgId := args[1] - err := install(pkgId, verbose, []string{}) - if err != nil { - fmt.Printf("[KFPM] %s\n", err.Error()) - } - - case "-r", "-u": - if len(args) < 2 { - fmt.Println("Error: -r/-u Requires A Package Name!") - return - } - pkg := args[1] - - if !isInstalled(pkg) { - fmt.Println("[KFPM] Package ID Not Installed.") - return - } - - if runScript(pkg, "uninstall", verbose) { - fmt.Println("[KFPM] Removal Success!") - removeInstalled(pkg) - setStatus("packageUninstallStatus", "success") - } else { - fmt.Println("[KFPM] Removal Failure!") - setStatus("packageUninstallStatus", "failure") - } - - case "-l": - listInstalled() - - case "-a": - listAvailable() - - case "-abi": - fmt.Printf("[KFPM] ABI: %s\n", ABI) - setStatus("deviceABI", ABI) - - default: - fmt.Println("Unknown Option:", args[0]) - help() - } + ensureInstalledDir() + checkRepositoryFile() + + registry = fetchAllRegistries() + installed = getInstalled() + ABI = fetchABI() + args := os.Args[1:] + + if len(args) == 0 { + help() + return + } + + verbose := len(args) > 2 && args[2] == "-v" + + switch args[0] { + case "-i": + if len(args) < 2 { + fmt.Println("Oops! -i Requires A Package Name!") + return + } + pkgId := args[1] + err := install(pkgId, verbose, []string{}) + if err != nil { + fmt.Printf("[KFPM] %s\n", err.Error()) + } + + case "-r", "-u": + if len(args) < 2 { + fmt.Println("Error: -r/-u Requires A Package Name!") + return + } + pkgId := args[1] + + if !isInstalled(pkgId) { + fmt.Println("[KFPM] Package ID Not Installed.") + return + } + pkg, err := getPackage(pkgId) + if err != nil { + fmt.Println("package not found main case ru", err) + return + } + + if runScript(pkg, "uninstall", verbose) { + fmt.Println("[KFPM] Removal Success!") + removeInstalled(pkgId) + setStatus("packageUninstallStatus", "success") + } else { + fmt.Println("[KFPM] Removal Failure!") + setStatus("packageUninstallStatus", "failure") + } + + case "-l": + listInstalled() + + case "-a": + listAvailable() + + case "-abi": + fmt.Printf("[KFPM] ABI: %s\n", ABI) + setStatus("deviceABI", ABI) + + default: + fmt.Println("Unknown Option:", args[0]) + help() + } } func help() { - fmt.Println(`KindleForge Package Manager + fmt.Println(`KindleForge Package Manager ==================== v1.1b, made by Penguins184, ThatPotatoDev @@ -114,198 +123,247 @@ kfpm -a Lists All Available Packages`) // Ensure Data Directory Exists func ensureInstalledDir() { - os.MkdirAll("/mnt/us/.KFPM", 0755) + os.MkdirAll("/mnt/us/.KFPM", 0755) +} + +func checkRepositoryFile() { + _, err := os.Stat(registryFile) + + if os.IsNotExist(err) { + f, err := os.OpenFile(registryFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + fmt.Println("error opening registry file", err) + return + } + defer f.Close() + defaultRepoURL := "https://kf.penguins184.xyz/" + _, err2 := f.WriteString(defaultRepoURL) + + if err2 != nil { + fmt.Println("error writing default repo file", err2) + return + } + } +} + +func fetchAllRegistries() []Package { + data, err := os.ReadFile(registryFile) + if err != nil { + fmt.Println("Couldn't open registry list, fetchallregistires", err) + return nil + } + + urls := strings.Split(strings.TrimSpace(string(data)), "\n") + var allPackageList []Package + + for _, url := range urls { + baseURL := strings.TrimSpace(url) + //blank line + if baseURL == "" { + continue + } + + pkgs := fetchRegistry(baseURL) + if pkgs != nil { + allPackageList = append(allPackageList, pkgs...) + } + } + return allPackageList } func install(pkgId string, verbose bool, loopedDeps []string) error { - if isInstalled(pkgId) { - fmt.Printf("[KFPM] Package '%s' Is Already Installed, Skipping\n", pkgId) - return nil - } - - if slices.Contains(loopedDeps, pkgId) { - return errors.New("Dependency Loop Detected, Aborting") - } - - loopedDeps = append(loopedDeps, pkgId) - - pkg, err := getPackage(pkgId) - - if err != nil { - return fmt.Errorf("Invalid Package ID '%s'!", pkgId) - } - - if len(pkg.ABI) == 0 { - pkg.ABI = []string{"sf", "hf"} - } - - if !slices.Contains(pkg.ABI, ABI) { - return fmt.Errorf("Package '%s' Does Not Support Device ABI!", pkgId) - } - - for _, depId := range pkg.Dependencies { - if err := install(depId, verbose, loopedDeps); err != nil { - return err - } - } - - if runScript(pkgId, "install", verbose) { - fmt.Printf("[KFPM] Successfully Installed '%s'!\n", pkgId) - appendInstalled(pkgId) - setStatus("packageInstallStatus", "success") - return nil - } else { - setStatus("packageInstallStatus", "failure") - return errors.New("Failed to install!") - } + if isInstalled(pkgId) { + fmt.Printf("[KFPM] Package '%s' Is Already Installed, Skipping\n", pkgId) + return nil + } + + if slices.Contains(loopedDeps, pkgId) { + return errors.New("Dependency Loop Detected, Aborting") + } + + loopedDeps = append(loopedDeps, pkgId) + + pkg, err := getPackage(pkgId) + + if err != nil { + return fmt.Errorf("Invalid Package ID '%s'!", pkgId) + } + + if len(pkg.ABI) == 0 { + pkg.ABI = []string{"sf", "hf"} + } + + if !slices.Contains(pkg.ABI, ABI) { + return fmt.Errorf("Package '%s' Does Not Support Device ABI!", pkgId) + } + + for _, depId := range pkg.Dependencies { + if err := install(depId, verbose, loopedDeps); err != nil { + return err + } + } + + if runScript(pkg, "install", verbose) { + fmt.Printf("[KFPM] Successfully Installed '%s'!\n", pkgId) + appendInstalled(pkgId) + setStatus("packageInstallStatus", "success") + return nil + } else { + setStatus("packageInstallStatus", "failure") + return errors.New("Failed to install!") + } } // Install/Uninstall Runners -func runScript(pkg string, action string, verbose bool) bool { - url := fmt.Sprintf("%s%s/%s.sh", registryBase, pkg, action) - cmd := exec.Command("/bin/sh", "-c", "curl -fSL --progress-bar "+url+" | sh") +func runScript(pkg Package, action string, verbose bool) bool { + url := fmt.Sprintf("%s%s/%s.sh", pkg.Repo, pkg.Uri, action) + cmd := exec.Command("/bin/sh", "-c", "curl -fSL --progress-bar "+url+" | sh") - if verbose { - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - } + if verbose { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } - err := cmd.Run() - return err == nil + err := cmd.Run() + return err == nil } // Append Package To List func appendInstalled(pkg string) { - data, _ := os.ReadFile(installedFile) - text := strings.TrimSpace(string(data)) - - lines := strings.Split(text, "\n") - for _, line := range lines { - if strings.TrimSpace(line) == pkg { - return - } - } - - f, err := os.OpenFile(installedFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return - } - defer f.Close() - - // Ensure Newline - if len(text) > 0 && !strings.HasSuffix(text, "\n") { - f.WriteString("\n") - } - - f.WriteString(strings.TrimSpace(pkg) + "\n") - installed = getInstalled() + data, _ := os.ReadFile(installedFile) + text := strings.TrimSpace(string(data)) + + lines := strings.Split(text, "\n") + for _, line := range lines { + if strings.TrimSpace(line) == pkg { + return + } + } + + f, err := os.OpenFile(installedFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return + } + defer f.Close() + + // Ensure Newline + if len(text) > 0 && !strings.HasSuffix(text, "\n") { + f.WriteString("\n") + } + + f.WriteString(strings.TrimSpace(pkg) + "\n") + installed = getInstalled() } // Remove Package From List func removeInstalled(pkg string) { - data, err := os.ReadFile(installedFile) - if err != nil { - return - } - - lines := strings.Split(strings.TrimSpace(string(data)), "\n") - var out []string - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if trimmed != "" && trimmed != pkg { - out = append(out, trimmed) - } - } - - os.WriteFile(installedFile, []byte(strings.Join(out, "\n")+"\n"), 0644) + data, err := os.ReadFile(installedFile) + if err != nil { + return + } + + lines := strings.Split(strings.TrimSpace(string(data)), "\n") + var out []string + for _, line := range lines { + trimmed := strings.TrimSpace(line) + if trimmed != "" && trimmed != pkg { + out = append(out, trimmed) + } + } + + os.WriteFile(installedFile, []byte(strings.Join(out, "\n")+"\n"), 0644) } // List Installed Packages func listInstalled() { - data, err := os.ReadFile(installedFile) - if err != nil || len(strings.TrimSpace(string(data))) == 0 { - fmt.Println("[KFPM] No Installed Packages Found!") - return - } - - lines := strings.Split(strings.TrimSpace(string(data)), "\n") - fmt.Println("Installed Packages:") - for i, line := range lines { - trimmed := strings.TrimSpace(line) - if trimmed != "" { - fmt.Printf("%d. %s\n", i+1, trimmed) - } - } + data, err := os.ReadFile(installedFile) + if err != nil || len(strings.TrimSpace(string(data))) == 0 { + fmt.Println("[KFPM] No Installed Packages Found!") + return + } + + lines := strings.Split(strings.TrimSpace(string(data)), "\n") + fmt.Println("Installed Packages:") + for i, line := range lines { + trimmed := strings.TrimSpace(line) + if trimmed != "" { + fmt.Printf("%d. %s\n", i+1, trimmed) + } + } } // List Available Packages From Remote func listAvailable() { - pkgs := registry - if pkgs == nil { - return - } - - fmt.Println("Available Packages:") - for i, p := range pkgs { - fmt.Printf("%d. %s\n", i+1, p.Name) - fmt.Printf(" - Description: %s\n", p.Description) - fmt.Printf(" - Author: %s\n", p.Author) - fmt.Printf(" - ID: %s\n", p.Uri) - fmt.Printf(" - Dependencies: %s\n", p.Dependencies) - fmt.Printf(" - ABI: %s\n\n", p.ABI) - } + pkgs := registry + if pkgs == nil { + return + } + + fmt.Println("Available Packages:") + for i, p := range pkgs { + fmt.Printf("%d. %s\n", i+1, p.Name) + fmt.Printf(" - Description: %s\n", p.Description) + fmt.Printf(" - Author: %s\n", p.Author) + fmt.Printf(" - ID: %s\n", p.Uri) + fmt.Printf(" - Dependencies: %s\n", p.Dependencies) + fmt.Printf(" - ABI: %s\n\n", p.ABI) + } } // Helpers -func fetchRegistry() []Package { - resp, err := http.Get(registryURL) - if err != nil { - fmt.Println("[KFPM] Failed To Fetch Registry:", err) - return nil - } - defer resp.Body.Close() - - body, _ := io.ReadAll(resp.Body) - var pkgs []Package - if err := json.Unmarshal(body, &pkgs); err != nil { - fmt.Println("[KFPM] Invalid Registry Format:", err) - return nil - } - return pkgs +func fetchRegistry(baseURL string) []Package { + resp, err := http.Get(baseURL + "/registry.json") + if err != nil { + fmt.Println("[KFPM] Failed To Fetch Registry:", err) + return nil + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + var pkgs []Package + if err := json.Unmarshal(body, &pkgs); err != nil { + fmt.Println("[KFPM] Invalid Registry Format:", err) + return nil + } + + for i := range pkgs { + pkgs[i].Repo = baseURL + } + return pkgs } func getPackage(id string) (Package, error) { - for _, p := range registry { - if p.Uri == id { - return p, nil - } - } - return Package{}, errors.New("Package Not Found!") + for _, p := range registry { + if p.Uri == id { + return p, nil + } + } + return Package{}, errors.New("Package Not Found!") } func isInstalled(id string) bool { - return slices.Contains(installed, id) + return slices.Contains(installed, id) } func getInstalled() []string { - data, err := os.ReadFile(installedFile) - if err != nil { - return nil - } - return strings.Split(strings.TrimSpace(string(data)), "\n") + data, err := os.ReadFile(installedFile) + if err != nil { + return nil + } + return strings.Split(strings.TrimSpace(string(data)), "\n") } func setStatus(prop string, status string) { - exec.Command( - "/bin/sh", "-c", - fmt.Sprintf(`lipc-set-prop xyz.penguins184.kindleforge %s -s "%s"`, prop, status), - ).Run() + exec.Command( + "/bin/sh", "-c", + fmt.Sprintf(`lipc-set-prop xyz.penguins184.kindleforge %s -s "%s"`, prop, status), + ).Run() } func fetchABI() string { - if _, err := os.Stat("/lib/ld-linux-armhf.so.3"); !os.IsNotExist(err) { - return "hf" - } - return "sf" + if _, err := os.Stat("/lib/ld-linux-armhf.so.3"); !os.IsNotExist(err) { + return "hf" + } + return "sf" } diff --git a/KindleForge/KindleForge/binaries/KFPM b/KindleForge/KindleForge/binaries/KFPM index d97c0e4..c16c9be 100644 Binary files a/KindleForge/KindleForge/binaries/KFPM and b/KindleForge/KindleForge/binaries/KFPM differ diff --git a/KindleForge/KindleForge/script.js b/KindleForge/KindleForge/script.js index b878b1a..275d3bc 100644 --- a/KindleForge/KindleForge/script.js +++ b/KindleForge/KindleForge/script.js @@ -45,6 +45,9 @@ function update() { window.kindle.messaging.sendMessage("com.lab126.chromebar", "configureChrome", chromebar); } +const installedFileURL = "file:///mnt/us/.KFPM/installed.txt"; +const registriesFileURL = "file:///mnt/us/.KFPM/repositories.txt"; + window.kindle.appmgr.ongo = function() { update(); window.kindle.messaging.receiveMessage("systemMenuItemSelected", function(eventType, id) { @@ -55,18 +58,7 @@ window.kindle.appmgr.ongo = function() { pkgs = []; lock = false; - _fetch( - "https://kf.penguins184.xyz/registry.json", - function() { - _file("file:///mnt/us/.KFPM/installed.txt").then(function(data) { - var joined = data.replace(/\d+\.\s*/g, "\n").trim(); - var installed = joined.split(/\n+/).map(function(line) { - return line.replace(/^\d+\.\s*/, "").trim(); - }).filter(Boolean); - render(installed); - }); - } - ); + loadAllRegistries(registriesFileURL) } else if (id === "KFORGE_UPDATE") { window.kindle.messaging.sendStringMessage("com.kindlemodding.utild", "runCMD", "curl https://kf.penguins184.xyz/update.sh | sh"); }; @@ -130,7 +122,7 @@ function isPackageSupported(pkgsJson, pkg, loopedDeps) { loopedDeps = loopedDeps.slice(); loopedDeps.push(pkg.uri); - + var deps = pkg.dependencies || []; for (var i = 0; i < deps.length; i++) { var dep = getPackage(deps[i], pkgsJson); @@ -150,14 +142,13 @@ function _fetch(url, cb) { try { var tempPkgs = JSON.parse(xhr.responseText); for (var i = 0; i < tempPkgs.length; i++) { - var pkg = tempPkgs[i]; + var pkg = tempPkgs[i]; if (!isPackageSupported(tempPkgs, pkg, [])) continue; pkgs.push(pkg); } if (cb) cb(); - else init(); } catch (e) { console.log("JSON Parse Failed", e); } @@ -185,15 +176,6 @@ function _file(url) { }); } -function init() { - _file("file:///mnt/us/.KFPM/installed.txt").then(function(data) { - var joined = data.replace(/\d+\.\s*/g, "\n").trim(); - var installed = joined.split(/\n+/).map(function(line) { - return line.replace(/^\d+\.\s*/, "").trim(); - }).filter(Boolean); - render(installed); - }); -} function render(installed) { var icons = { @@ -269,7 +251,7 @@ function render(installed) { var pkgId = btn.getAttribute("data-id"); var name = btn.getAttribute("data-name"); var wasInstalled = btn.getAttribute("data-installed") === "true"; - + if (lock) { btn.innerHTML = icons.progress + " Another Operation In Progress..."; btn.blur(); btn.offsetHeight; //Blur & Reflow @@ -279,36 +261,36 @@ function render(installed) { btn.offsetHeight; //Ensure Reflow }); }); - + setTimeout(function() { btn.innerHTML = (wasInstalled ? icons.x : icons.download) + (wasInstalled ? " Uninstall Package" : " Install Package"); }, 2000); - - setTimeout(function() {}, 50); //UI Update Time + + setTimeout(function() { }, 50); //UI Update Time return; } - + lock = true; btn.disabled = true; - + var action = wasInstalled ? "-r" : "-i"; btn.innerHTML = icons.progress + (wasInstalled ? " Uninstalling " : " Installing ") + name + "..."; - + btn.offsetHeight; //Reflow - + var eventName = wasInstalled ? "packageUninstallStatus" : "packageInstallStatus"; (window.kindle || top.kindle).messaging.receiveMessage( eventName, function(eventType, data) { lock = false; btn.disabled = false; - + var success = typeof data === "string" && data.indexOf("success") !== -1; if (success) { @@ -318,7 +300,7 @@ function render(installed) { (wasInstalled ? " Install Package" : " Uninstall Package"); - + // Update dependency buttons if (!wasInstalled) { var deps = getPackage(pkgId, pkgs).dependencies || []; @@ -330,7 +312,7 @@ function render(installed) { btn.offsetHeight; //Reflow } } - + } else { btn.innerHTML = icons.x + @@ -342,7 +324,7 @@ function render(installed) { } } ); - + setTimeout(function() { (window.kindle || top.kindle).messaging.sendStringMessage( "com.kindlemodding.utild", @@ -359,10 +341,52 @@ function render(installed) { gCard(cIndex); } +function dataLinesToArray(data) { + var list = data.split(/\n+/).map(function(line) { + return line.trim(); + }).filter(Boolean); + return list; +} + +function loadAllRegistries(fileUrl) { + _file(fileUrl).then(function(data) { + var repos = dataLinesToArray(data) + + var numberOfRepos = repos.length; + + if (numberOfRepos == 0) { + _file(installedFileURL).then(function(data) { + installed = dataLinesToArray(data); + render(installed); + }); + console.error("0 repos!"); + return; + } + + var reposToLoad = numberOfRepos; + + function oneRepoLoaded() { + reposToLoad--; + if (reposToLoad == 0) { + _file(installedFileURL).then(function(data) { + var installed = dataLinesToArray(data); + render(installed); + }); + }; + }; + repos.forEach(function(url) { + _fetch(url + "/registry.json", oneRepoLoaded); + }); + + }); +}; + document.addEventListener("DOMContentLoaded", function() { (window.kindle || top.kindle).messaging.receiveMessage("deviceABI", function(eventType, ABI) { deviceABI = ABI; document.getElementById("abi-status").innerText = "ABI: " + ABI; + + loadAllRegistries(registriesFileURL) }); setTimeout(function() { @@ -372,9 +396,6 @@ document.addEventListener("DOMContentLoaded", function() { "/var/local/mesquite/KindleForge/binaries/KFPM -abi" ); }, 10); - - _fetch( - "https://kf.penguins184.xyz/registry.json" - ); + document.getElementById("js-status").innerText = "JS Working!"; });