revamp
This commit is contained in:
parent
16b79c8eaa
commit
83e3e3ca78
9 changed files with 450 additions and 40 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,3 @@
|
|||
sup
|
||||
bob
|
||||
*.o
|
||||
*.tar.gz
|
||||
|
|
9
Makefile
9
Makefile
|
@ -3,19 +3,20 @@
|
|||
|
||||
CC = cc
|
||||
VERSION = 1.0
|
||||
TARGET = sup
|
||||
TARGET = bob
|
||||
MANPAGE = $(TARGET).1
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
MANDIR = $(PREFIX)/share/man/man1
|
||||
|
||||
# Flags
|
||||
CFLAGS = -O3 -march=native -mtune=native -pipe -s -std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600
|
||||
LDFLAGS = $(shell pkg-config --libs libcurl)
|
||||
CFLAGS = -O3 -march=native -mtune=native -pipe -g -std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600
|
||||
|
||||
SRC = sup.c
|
||||
SRC = bob.c
|
||||
|
||||
$(TARGET): $(SRC)
|
||||
$(CC) $(SRC) -o $@ $(CFLAGS)
|
||||
$(CC) $(SRC) -o $@ $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
dist:
|
||||
mkdir -p $(TARGET)-$(VERSION)
|
||||
|
|
24
README.md
24
README.md
|
@ -1,6 +1,24 @@
|
|||
# sup
|
||||
# bob
|
||||
|
||||
sup(System Utilities Packager) is a package manager that is for system utilities software.
|
||||
bob is a **B**inary **O**nly package manager that is for managing my publicly published softwares. Sample repository can be found on [here](https://github.com/night0721/bob-packages), only works on musl-libc system.
|
||||
|
||||
It is recommended to have `XDG_DATA_HOME` defined to store the database for bob, otherwise database file would be created at `~/.cache`
|
||||
|
||||
# Customizing
|
||||
|
||||
You may use your own Github repository for supplying the binaries, or other platforms but requiring you to modify the source to use other URL to download.
|
||||
|
||||
All these customizations can be done in `bob.h`
|
||||
|
||||
# Usage
|
||||
```sh
|
||||
Usage:
|
||||
(install|i|add) <package> Install a package
|
||||
(uninstall|d|del) <package> Uninstall a package
|
||||
(update|u) <package> Update a package
|
||||
(search|s) <package> Search for a package
|
||||
(list|l) [all] List all packages installed/available
|
||||
```
|
||||
|
||||
# Dependencies
|
||||
|
||||
|
@ -20,4 +38,4 @@ $ make
|
|||
Contributions are welcomed, feel free to open a pull request.
|
||||
|
||||
# License
|
||||
This project is licensed under the GNU Public License v3.0. See [LICENSE](https://github.com/night0721/sup/blob/master/LICENSE) for more information.
|
||||
This project is licensed under the GNU Public License v3.0. See [LICENSE](https://github.com/night0721/bob/blob/master/LICENSE) for more information.
|
||||
|
|
26
bob.1
Normal file
26
bob.1
Normal file
|
@ -0,0 +1,26 @@
|
|||
.TH bob 1 bob\-1.0.0
|
||||
.SH NAME
|
||||
bob \- Binary Only Packager Manager
|
||||
.SH SYNOPSIS
|
||||
.B bob
|
||||
.RB [ install|i|add ]
|
||||
.RB [ uninstall|d|del ]
|
||||
.RB [ update|u ]
|
||||
.RB [ search|s ]
|
||||
.RB [ list|s ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
bob is a Binary Only package manager that is for managing my publicly published softwares.
|
||||
.PP
|
||||
.SH USAGE
|
||||
.
|
||||
.nf
|
||||
(install|i|add) <package> Install a package
|
||||
(uninstall|d|del) <package> Uninstall a package
|
||||
(update|u) <package> Update a package
|
||||
(search|s) <package> Search for a package
|
||||
(list|l) [all] List all packages installed/available
|
||||
|
||||
.SH AUTHOR
|
||||
Made by Night Kaly
|
||||
.B <night@night0721.xyz>
|
377
bob.c
Normal file
377
bob.c
Normal file
|
@ -0,0 +1,377 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "bob.h"
|
||||
|
||||
void debug(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stderr, GRN "[bob] " CRESET);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void error(const char *msg, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
fprintf(stderr, RED "[bob] " CRESET);
|
||||
vfprintf(stderr, msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/*
|
||||
* For curl to write data
|
||||
*/
|
||||
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream)
|
||||
{
|
||||
size_t written = fwrite(ptr, size, nmemb, stream);
|
||||
return written;
|
||||
}
|
||||
|
||||
char *get_db_path()
|
||||
{
|
||||
char *db_dir = getenv("XDG_DATA_HOME");
|
||||
char *db_path = malloc(PATH_MAX * sizeof(char));
|
||||
if (db_path == NULL) {
|
||||
error("Error allocating memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* make db idr at .cache/bob if XDG_DATA_HOME isn't defined */
|
||||
if (db_dir) {
|
||||
snprintf(db_path, PATH_MAX, "%s/bob", db_dir);
|
||||
} else {
|
||||
snprintf(db_path, PATH_MAX, "%s/.cache/bob", getenv("HOME"));
|
||||
}
|
||||
|
||||
/* create the directory if it doesn't exist */
|
||||
mkdir(db_path, 0755);
|
||||
strcat(db_path, "/bob.db");
|
||||
|
||||
return db_path;
|
||||
}
|
||||
|
||||
/*
|
||||
* act = 0 == delete
|
||||
* act = 1 == act
|
||||
*/
|
||||
int update_package_list(char *pkg, int act)
|
||||
{
|
||||
char *db_path = get_db_path();
|
||||
FILE *db_file = fopen(db_path, "a+");
|
||||
if (db_file == NULL) {
|
||||
error("fopen: %s\n", strerror(errno));
|
||||
free(db_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char line[MAX_PKG_NAME_LENGTH];
|
||||
char **entries = malloc(MAX_PKGS * sizeof(char *));
|
||||
int entries_count = 0;
|
||||
if (entries == NULL) {
|
||||
error("Error allocating memory\n");
|
||||
free(db_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int found = 0;
|
||||
while (fgets(line, sizeof(line), db_file)) {
|
||||
line[strcspn(line, "\n")] = '\0';
|
||||
char *name = strdup(line);
|
||||
if (name == NULL) {
|
||||
error("Error allocating memory\n");
|
||||
free(db_path);
|
||||
free(entries);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(line, pkg) == 0) {
|
||||
found = 1;
|
||||
/* don't add to entries if we are looking for it */
|
||||
if (!act) continue;
|
||||
}
|
||||
entries[entries_count] = name;
|
||||
entries_count++;
|
||||
}
|
||||
|
||||
if (act) {
|
||||
if (!found) {
|
||||
/* not duplicate and adding */
|
||||
fprintf(db_file, "%s\n", pkg);
|
||||
}
|
||||
goto done;
|
||||
} else {
|
||||
if (!found) {
|
||||
/* deleting but not here */
|
||||
error("Package %s is not installed\n", pkg);
|
||||
fclose(db_file);
|
||||
free(db_path);
|
||||
free(entries);
|
||||
return 1;
|
||||
} else {
|
||||
freopen(db_path, "w", db_file);
|
||||
for (int i = 0; i < entries_count; i++) {
|
||||
fprintf(db_file, "%s\n", entries[i]);
|
||||
free(entries[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
fclose(db_file);
|
||||
free(db_path);
|
||||
free(entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int download_file(char *pkg)
|
||||
{
|
||||
long response_code;
|
||||
|
||||
char url[256], dest[PATH_MAX];
|
||||
snprintf(dest, sizeof(dest), "%s%s", DEST_DIR, pkg);
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
if (curl) {
|
||||
FILE *dest_file = fopen(dest, "wb");
|
||||
if (dest_file == NULL) {
|
||||
error("fopen: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(url, sizeof(url), "https://raw.githubusercontent.com/%s/%s/%s", REPO, BRANCH, pkg);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, dest_file);
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
|
||||
int success = 0;
|
||||
if (res != CURLE_OK || response_code != 200) {
|
||||
error("Failed to download %s: %s (Error code %ld)\n", url, curl_easy_strerror(res), response_code);
|
||||
fclose(dest_file);
|
||||
remove(dest);
|
||||
} else {
|
||||
fclose(dest_file);
|
||||
debug("Downloaded %s to %s\n", url, dest);
|
||||
|
||||
/* make binary exectuable */
|
||||
if (chmod(dest, 0755) != 0) {
|
||||
error("chmod: %s\n", strerror(errno));
|
||||
} else {
|
||||
debug("Made %s executable\n", pkg);
|
||||
/* add to package list */
|
||||
if (update_package_list(pkg, 1) == 0) {
|
||||
debug("Added %s to package list\n", pkg);
|
||||
success = 1;
|
||||
} else {
|
||||
error("Failed to add %s to package list\n", pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
if (!success) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int install_package(char *pkg)
|
||||
{
|
||||
if (strcmp(pkg, "index") == 0) {
|
||||
error("'index' is reserved and cannot be installed\n");
|
||||
}
|
||||
debug("Installing %s\n", pkg);
|
||||
if (download_file(pkg) == 0) {
|
||||
debug("Installation completed\n");
|
||||
return 0;
|
||||
} else {
|
||||
error("Fail to install %s\n", pkg);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int uninstall_package(char *pkg)
|
||||
{
|
||||
char dest[PATH_MAX];
|
||||
snprintf(dest, sizeof(dest), "%s%s", DEST_DIR, pkg);
|
||||
|
||||
/* delete from package list */
|
||||
if (update_package_list(pkg, 0) == 0) {
|
||||
debug("Removed %s from package list\n", pkg);
|
||||
} else {
|
||||
error("Failed to remoe %s from package list\n", pkg);
|
||||
return 1;
|
||||
}
|
||||
debug("Uninstalling %s\n", pkg);
|
||||
if (remove(dest) == 0) {
|
||||
debug("Uninstalled %s\n", pkg);
|
||||
} else {
|
||||
error("remove: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void update_package(char *pkg)
|
||||
{
|
||||
if (uninstall_package(pkg) == 0) {
|
||||
install_package(pkg);
|
||||
} else {
|
||||
error("Cannot continue, please retry\n");
|
||||
}
|
||||
}
|
||||
|
||||
char **search_index()
|
||||
{
|
||||
long response_code;
|
||||
char temp_path[] = "/tmp/bob_index";
|
||||
|
||||
char url[256];
|
||||
|
||||
snprintf(url, sizeof(url), "https://raw.githubusercontent.com/%s/%s/index", REPO, BRANCH);
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
if (curl) {
|
||||
FILE *temp_f = fopen(temp_path, "wb");
|
||||
if (temp_f == NULL) {
|
||||
error("fopen: %s\n", strerror(errno));
|
||||
curl_easy_cleanup(curl);
|
||||
remove(temp_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, temp_f);
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
fclose(temp_f);
|
||||
|
||||
if (res != CURLE_OK || response_code != 200) {
|
||||
fprintf(stderr, "Failed to download repository index: %s (Error code %ld)\n", curl_easy_strerror(res), response_code);
|
||||
curl_easy_cleanup(curl);
|
||||
remove(temp_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
FILE *index_file = fopen(temp_path, "r");
|
||||
if (index_file == NULL) {
|
||||
error("fopen: %s\n", strerror(errno));
|
||||
remove(temp_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **entries = malloc(MAX_PKGS * sizeof(char *));
|
||||
int entries_count = 0;
|
||||
|
||||
if (entries == NULL) {
|
||||
error("Error allocating memory\n");
|
||||
return NULL;
|
||||
}
|
||||
char line[MAX_PKG_NAME_LENGTH];
|
||||
while (fgets(line, sizeof(line), index_file)) {
|
||||
line[strcspn(line, "\n")] = '\0';
|
||||
char *name = strdup(line);
|
||||
if (name == NULL) {
|
||||
error("Error allocating memory\n");
|
||||
return NULL;
|
||||
}
|
||||
entries[entries_count] = name;
|
||||
entries_count++;
|
||||
}
|
||||
entries[entries_count] = NULL;
|
||||
fclose(index_file);
|
||||
remove(temp_path);
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
void search_package(char *pkg)
|
||||
{
|
||||
debug("Searching for package: %s\n", pkg);
|
||||
char **index = search_index();
|
||||
for (int i = 0; index[i] != NULL; i++) {
|
||||
if (strcmp(pkg, index[i]) == 0) {
|
||||
debug("Package \"%s\" found in repository index\n", pkg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
error("Package \"%s\" not found in repository index\n", pkg);
|
||||
}
|
||||
|
||||
void list_package(char *pkg)
|
||||
{
|
||||
if (pkg == NULL) {
|
||||
/* list installed packages */
|
||||
char *db_path = get_db_path();
|
||||
FILE *db_file = fopen(db_path, "r");
|
||||
if (db_file == NULL) {
|
||||
error("fopen: %s\n", strerror(errno));
|
||||
free(db_path);
|
||||
return;
|
||||
}
|
||||
char line[MAX_PKG_NAME_LENGTH];
|
||||
|
||||
while (fgets(line, sizeof(line), db_file)) {
|
||||
line[strcspn(line, "\n")] = '\0';
|
||||
printf("%s\n", line);
|
||||
}
|
||||
} else if (strcmp(pkg, "all") == 0) {
|
||||
/* list all available packages */
|
||||
char **index = search_index();
|
||||
for (int i = 0; index[i] != NULL; i++) {
|
||||
printf("%s\n", index[i]);
|
||||
}
|
||||
} else {
|
||||
error("Unknown option\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "bob %s\n\nUsage:\n", VERSION);
|
||||
fprintf(stderr, " (install|i|add) <package>\tInstall a package\n");
|
||||
fprintf(stderr, " (uninstall|d|del) <package>\tUninstall a package\n");
|
||||
fprintf(stderr, " (update|u) <package>\t\tUpdate a package\n");
|
||||
fprintf(stderr, " (search|s) <package>\t\tSearch for a package\n");
|
||||
fprintf(stderr, " (list|l) [all] \t\tList all packages installed/available\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
char *command = argv[1];
|
||||
char *pkg = (argc > 2) ? argv[2] : NULL;
|
||||
|
||||
if ((strcmp(command, "install") == 0 || strcmp(command, "add") == 0 || strcmp(command, "i") == 0) && pkg) {
|
||||
install_package(pkg);
|
||||
} else if ((strcmp(command, "uninstall") == 0 || strcmp(command, "del") == 0 || strcmp(command, "d") == 0) && pkg) {
|
||||
uninstall_package(pkg);
|
||||
} else if ((strcmp(command, "update") == 0 || strcmp(command, "u") == 0) && pkg) {
|
||||
update_package(pkg);
|
||||
} else if ((strcmp(command, "search") == 0 || strcmp(command, "s") == 0) && pkg) {
|
||||
search_package(pkg);
|
||||
} else if ((strcmp(command, "list") == 0 || strcmp(command, "l") == 0)) {
|
||||
list_package(pkg);
|
||||
} else {
|
||||
error("Unknown command or package name missing\n");
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
20
bob.h
Normal file
20
bob.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef BOB_H_H
|
||||
#define BOB_H_H
|
||||
|
||||
#define VERSION "1.0.0"
|
||||
|
||||
/* Not recommend modifiying this unless you know what you are doing */
|
||||
#define MAX_PKG_NAME_LENGTH 128
|
||||
#define MAX_PKGS 128
|
||||
|
||||
/* Must be github repo */
|
||||
#define REPO "night0721/bob-packages"
|
||||
#define BRANCH "main"
|
||||
|
||||
#define DEST_DIR "/usr/local/bin/"
|
||||
|
||||
#define RED "\033[0;31m"
|
||||
#define GRN "\033[0;32m"
|
||||
#define CRESET "\033[0m"
|
||||
|
||||
#endif
|
14
sup.1
14
sup.1
|
@ -1,14 +0,0 @@
|
|||
.TH sup 1 sup\-1.0.0
|
||||
.SH NAME
|
||||
sup \- System Utilities Packager
|
||||
.SH SYNOPSIS
|
||||
.B sup
|
||||
|
||||
.SH DESCRIPTION
|
||||
sup is a package manager that is for system utilities software.
|
||||
|
||||
.SH OPTIONS
|
||||
|
||||
.SH AUTHOR
|
||||
Made by Night Kaly
|
||||
.B <night@night0721.xyz>
|
14
sup.c
14
sup.c
|
@ -1,14 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("sup!\n");
|
||||
return 0;
|
||||
}
|
4
sup.h
4
sup.h
|
@ -1,4 +0,0 @@
|
|||
#ifndef SUP_H_H
|
||||
#define SUP_H_H
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue